- Uses certificate and key files provided by admin in config - Let's Encrypt integration planned, but not even close to working - Initial HTTP3 Support added
This commit is contained in:
parent
68d7a5e8c3
commit
9151bfb3be
6 changed files with 250 additions and 42 deletions
117
auth-new/tls.go
117
auth-new/tls.go
|
@ -5,8 +5,10 @@ import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -40,62 +42,99 @@ func (l *leUser) GetPrivateKey() crypto.PrivateKey {
|
||||||
return l.Key
|
return l.Key
|
||||||
}
|
}
|
||||||
|
|
||||||
func TlsFromConfig() (*tls.Config, error) {
|
// Return the path to the certificate file (.crt), the key file (.key) and any errors
|
||||||
|
func TlsFromConfig() (string, string, error) {
|
||||||
if config.GlobalConfig.SSL.UseLetsEncrypt != nil && *config.GlobalConfig.SSL.UseLetsEncrypt {
|
if config.GlobalConfig.SSL.UseLetsEncrypt != nil && *config.GlobalConfig.SSL.UseLetsEncrypt {
|
||||||
return tlsFromLetsEncrypt()
|
return tlsFromLetsEncrypt()
|
||||||
|
} else {
|
||||||
|
return tlsFromFile()
|
||||||
}
|
}
|
||||||
panic("Not implemented")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func tlsFromLetsEncrypt() (*tls.Config, error) {
|
// Verifies that the relevant config entries are set and are paths to readable files
|
||||||
|
func tlsFromFile() (string, string, error) {
|
||||||
|
sslConf := config.GlobalConfig.SSL
|
||||||
|
if sslConf.CertificateFile == nil {
|
||||||
|
return "", "", errors.New("need a path to a certificate file to use for tls")
|
||||||
|
}
|
||||||
|
if sslConf.CertKeyFile == nil {
|
||||||
|
return "", "", errors.New("need a path to a certificate key file to use for tls")
|
||||||
|
}
|
||||||
|
certInfo, err := os.Stat(*sslConf.CertificateFile)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
if certInfo.IsDir() {
|
||||||
|
return "", "", errors.New("certificate file is a directory, needs to be a file")
|
||||||
|
}
|
||||||
|
keyInfo, err := os.Stat(*sslConf.CertKeyFile)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
if keyInfo.IsDir() {
|
||||||
|
return "", "", errors.New("key file is a directory, needs to be a file")
|
||||||
|
}
|
||||||
|
return *sslConf.CertificateFile, *sslConf.CertKeyFile, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the LetsEncrypt generated certifiates and store them in temporary files for the server to use
|
||||||
|
// If the certificate is too old, requests a new one.
|
||||||
|
func tlsFromLetsEncrypt() (string, string, error) {
|
||||||
metadata, err := dbgen.ServerMetadata.First()
|
metadata, err := dbgen.ServerMetadata.First()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", "", err
|
||||||
}
|
}
|
||||||
if !metadata.LELastUpdate.Valid {
|
if !metadata.LELastUpdate.Valid {
|
||||||
return generateLetsEncryptCert(metadata)
|
if err = generateLetsEncryptCert(metadata); err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
} else if metadata.LELastUpdate.Time.Add(time.Hour * 24 * 60).Before(time.Now()) {
|
} else if metadata.LELastUpdate.Time.Add(time.Hour * 24 * 60).Before(time.Now()) {
|
||||||
// Let's Encrypt certificates last for 90 days. It is recommended to renew every 60 days
|
// Let's Encrypt certificates last for 90 days. It is recommended to renew every 60 days
|
||||||
// See https://letsencrypt.org/docs/faq/#what-is-the-lifetime-for-let-s-encrypt-certificates-for-how-long-are-they-valid
|
// See https://letsencrypt.org/docs/faq/#what-is-the-lifetime-for-let-s-encrypt-certificates-for-how-long-are-they-valid
|
||||||
return generateLetsEncryptCert(metadata)
|
if err = generateLetsEncryptCert(metadata); err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
panic("Not implemented")
|
panic("Not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// (Re-)Generate a Lets Encrypt certificate using the provided details in the config
|
// (Re-)Generate a Lets Encrypt certificate using the provided details in the config.
|
||||||
// Automatically stores the newly generated certificate in the db, encrypting beforehand
|
// Automatically stores the newly generated certificate in the db, encrypting beforehand.
|
||||||
func generateLetsEncryptCert(metadata *models.ServerMetadata) (*tls.Config, error) {
|
// Also updates the metadata with the new data.
|
||||||
|
// Does not write the cert to a temporary file.
|
||||||
|
func generateLetsEncryptCert(metadata *models.ServerMetadata) error {
|
||||||
|
encKey := []byte(config.GlobalConfig.Storage.EncryptionKey)
|
||||||
// Follow https://go-acme.github.io/lego/usage/library/index.html
|
// Follow https://go-acme.github.io/lego/usage/library/index.html
|
||||||
var userPrivateKey *ecdsa.PrivateKey
|
var userPrivateKey *ecdsa.PrivateKey
|
||||||
var err error
|
var err error
|
||||||
if len(metadata.LEUserPrivKey) == 0 {
|
if len(metadata.LEUserPrivKey) == 0 {
|
||||||
userPrivateKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
userPrivateKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
marshalled, err := x509.MarshalECPrivateKey(userPrivateKey)
|
marshalled, err := x509.MarshalECPrivateKey(userPrivateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
encryptedKey, err := Encrypt(
|
encryptedKey, err := Encrypt(
|
||||||
[]byte(config.GlobalConfig.Storage.EncryptionKey),
|
encKey,
|
||||||
marshalled,
|
marshalled,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
metadata.LEUserPrivKey = encryptedKey
|
metadata.LEUserPrivKey = encryptedKey
|
||||||
} else {
|
} else {
|
||||||
decryptedKey, err := Decrypt([]byte(config.GlobalConfig.Storage.EncryptionKey), metadata.LEUserPrivKey)
|
decryptedKey, err := Decrypt(encKey, metadata.LEUserPrivKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
userPrivateKey, err = x509.ParseECPrivateKey(decryptedKey)
|
userPrivateKey, err = x509.ParseECPrivateKey(decryptedKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
user := leUser{Email: *config.GlobalConfig.SSL.AdminMail}
|
user := leUser{Email: *config.GlobalConfig.SSL.AdminMail, Key: userPrivateKey}
|
||||||
leConfig := lego.NewConfig(&user)
|
leConfig := lego.NewConfig(&user)
|
||||||
leConfig.CADirURL = strings.Replace(
|
leConfig.CADirURL = strings.Replace(
|
||||||
config.GlobalConfig.General.GetFullPublicUrl(),
|
config.GlobalConfig.General.GetFullPublicUrl(),
|
||||||
|
@ -107,22 +146,22 @@ func generateLetsEncryptCert(metadata *models.ServerMetadata) (*tls.Config, erro
|
||||||
|
|
||||||
leClient, err := lego.NewClient(leConfig)
|
leClient, err := lego.NewClient(leConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
// TODO: Figure out these servers
|
// TODO: Figure out these servers
|
||||||
err = leClient.Challenge.SetHTTP01Provider(http01.NewProviderServer("", "5002"))
|
err = leClient.Challenge.SetHTTP01Provider(http01.NewProviderServer("", "5002"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
err = leClient.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", "5001"))
|
err = leClient.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", "5001"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
reg, err := leClient.Registration.Register(
|
reg, err := leClient.Registration.Register(
|
||||||
registration.RegisterOptions{TermsOfServiceAgreed: true},
|
registration.RegisterOptions{TermsOfServiceAgreed: true},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
user.Registration = reg
|
user.Registration = reg
|
||||||
req := certificate.ObtainRequest{
|
req := certificate.ObtainRequest{
|
||||||
|
@ -131,9 +170,35 @@ func generateLetsEncryptCert(metadata *models.ServerMetadata) (*tls.Config, erro
|
||||||
}
|
}
|
||||||
certificates, err := leClient.Certificate.Obtain(req)
|
certificates, err := leClient.Certificate.Obtain(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
_ = certificates
|
metadata.LELastUpdate = sql.NullTime{Valid: true, Time: time.Now()}
|
||||||
// TODO: Do something with certificates here
|
metadata.LEDomain = certificates.Domain
|
||||||
panic("Not implemented")
|
metadata.LECertStableUrl = certificates.CertStableURL
|
||||||
|
metadata.LECertUrl = certificates.CertURL
|
||||||
|
metadata.LECSR, err = Encrypt(encKey, certificates.CSR)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
metadata.LECertificate, err = Encrypt(encKey, certificates.Certificate)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
metadata.LECertificate, err = Encrypt(encKey, certificates.Certificate)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
metadata.LEPrivateKey, err = Encrypt(encKey, certificates.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
metadata.LEIssuerCertificate, err = Encrypt(encKey, certificates.IssuerCertificate)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = dbgen.ServerMetadata.Save(metadata)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,10 @@ type ConfigSSL struct {
|
||||||
HandleSSL bool `toml:"handle_ssl"` // Whether Linstrom should handle SSL encryption itself in case of http1/2
|
HandleSSL bool `toml:"handle_ssl"` // Whether Linstrom should handle SSL encryption itself in case of http1/2
|
||||||
// If Linstrom is to handle SSL, whether it should use LetsEncrypt for certificates
|
// If Linstrom is to handle SSL, whether it should use LetsEncrypt for certificates
|
||||||
UseLetsEncrypt *bool `toml:"use_lets_encrypt"`
|
UseLetsEncrypt *bool `toml:"use_lets_encrypt"`
|
||||||
// Path to the certificate if Linstrom is to handle SSL while not using LetsEncrypt
|
// Path to the certificate file (.crt) if Linstrom is to handle SSL while not using LetsEncrypt
|
||||||
CertificateFile *string `toml:"certificate_file"`
|
CertificateFile *string `toml:"certificate_file"`
|
||||||
|
// Path to the certificate key file (.key) to handle SSL while not using LetsEncrypt
|
||||||
|
CertKeyFile *string `toml:"cert_key_file"`
|
||||||
// Mail adress to use in case of using LetsEncrypt
|
// Mail adress to use in case of using LetsEncrypt
|
||||||
AdminMail *string `toml:"admin_mail"`
|
AdminMail *string `toml:"admin_mail"`
|
||||||
}
|
}
|
||||||
|
@ -251,6 +253,14 @@ func (gc *ConfigGeneral) GetFullPublicUrl() string {
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gc *ConfigGeneral) GetFinalPublicPort() int {
|
||||||
|
if gc.PublicPort != nil {
|
||||||
|
return *gc.PublicPort
|
||||||
|
} else {
|
||||||
|
return gc.PrivatePort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (sc *ConfigStorage) BuildPostgresDSN() string {
|
func (sc *ConfigStorage) BuildPostgresDSN() string {
|
||||||
dsn := fmt.Sprintf(
|
dsn := fmt.Sprintf(
|
||||||
"host=%s user=%s password=%s dbname=%s port=%d",
|
"host=%s user=%s password=%s dbname=%s port=%d",
|
||||||
|
|
6
go.mod
6
go.mod
|
@ -33,11 +33,13 @@ require (
|
||||||
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
|
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
|
||||||
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.9.2 // indirect
|
github.com/go-sql-driver/mysql v1.9.2 // indirect
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||||
github.com/go-webauthn/x v0.1.20 // indirect
|
github.com/go-webauthn/x v0.1.20 // indirect
|
||||||
github.com/goccy/go-json v0.10.5 // indirect
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||||
github.com/google/go-cmp v0.7.0 // indirect
|
github.com/google/go-cmp v0.7.0 // indirect
|
||||||
github.com/google/go-tpm v0.9.3 // indirect
|
github.com/google/go-tpm v0.9.3 // indirect
|
||||||
|
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
|
@ -52,8 +54,12 @@ require (
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/miekg/dns v1.1.64 // indirect
|
github.com/miekg/dns v1.1.64 // indirect
|
||||||
|
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
|
||||||
|
github.com/quic-go/qpack v0.5.1 // indirect
|
||||||
|
github.com/quic-go/quic-go v0.52.0 // indirect
|
||||||
github.com/segmentio/asm v1.2.0 // indirect
|
github.com/segmentio/asm v1.2.0 // indirect
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
|
go.uber.org/mock v0.5.0 // indirect
|
||||||
golang.org/x/mod v0.24.0 // indirect
|
golang.org/x/mod v0.24.0 // indirect
|
||||||
golang.org/x/net v0.39.0 // indirect
|
golang.org/x/net v0.39.0 // indirect
|
||||||
golang.org/x/sync v0.13.0 // indirect
|
golang.org/x/sync v0.13.0 // indirect
|
||||||
|
|
18
go.sum
18
go.sum
|
@ -10,8 +10,12 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
||||||
|
@ -27,6 +31,8 @@ github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JS
|
||||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
|
github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
|
||||||
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||||
github.com/go-webauthn/webauthn v0.12.3 h1:hHQl1xkUuabUU9uS+ISNCMLs9z50p9mDUZI/FmkayNE=
|
github.com/go-webauthn/webauthn v0.12.3 h1:hHQl1xkUuabUU9uS+ISNCMLs9z50p9mDUZI/FmkayNE=
|
||||||
github.com/go-webauthn/webauthn v0.12.3/go.mod h1:4JRe8Z3W7HIw8NGEWn2fnUwecoDzkkeach/NnvhkqGY=
|
github.com/go-webauthn/webauthn v0.12.3/go.mod h1:4JRe8Z3W7HIw8NGEWn2fnUwecoDzkkeach/NnvhkqGY=
|
||||||
github.com/go-webauthn/x v0.1.20 h1:brEBDqfiPtNNCdS/peu8gARtq8fIPsHz0VzpPjGvgiw=
|
github.com/go-webauthn/x v0.1.20 h1:brEBDqfiPtNNCdS/peu8gARtq8fIPsHz0VzpPjGvgiw=
|
||||||
|
@ -44,8 +50,11 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/go-tpm v0.9.3 h1:+yx0/anQuGzi+ssRqeD6WpXjW2L/V0dItUayO0i9sRc=
|
github.com/google/go-tpm v0.9.3 h1:+yx0/anQuGzi+ssRqeD6WpXjW2L/V0dItUayO0i9sRc=
|
||||||
github.com/google/go-tpm v0.9.3/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
|
github.com/google/go-tpm v0.9.3/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
|
||||||
|
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||||
|
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||||
|
@ -88,12 +97,18 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/nrednav/cuid2 v1.0.1 h1:aYLDCmGxEij7xCdiV6GVSPSlqFOS6sqHKKvBeKjddVY=
|
github.com/nrednav/cuid2 v1.0.1 h1:aYLDCmGxEij7xCdiV6GVSPSlqFOS6sqHKKvBeKjddVY=
|
||||||
github.com/nrednav/cuid2 v1.0.1/go.mod h1:nH9lUYqbtoVsnpy20etw5q1guTjE99Xy4EpmnK5nKm0=
|
github.com/nrednav/cuid2 v1.0.1/go.mod h1:nH9lUYqbtoVsnpy20etw5q1guTjE99Xy4EpmnK5nKm0=
|
||||||
|
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
|
||||||
|
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
|
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
|
||||||
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||||
|
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||||
|
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||||
|
github.com/quic-go/quic-go v0.52.0 h1:/SlHrCRElyaU6MaEPKqKr9z83sBg2v4FLLvWM+Z47pA=
|
||||||
|
github.com/quic-go/quic-go v0.52.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ=
|
||||||
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||||
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
||||||
|
@ -114,6 +129,8 @@ github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcY
|
||||||
github.com/yaronf/httpsign v0.3.2 h1:rBYYx9eHm60noI4oC24JAD2tJuM8AUDh9ErJO/FfzBs=
|
github.com/yaronf/httpsign v0.3.2 h1:rBYYx9eHm60noI4oC24JAD2tJuM8AUDh9ErJO/FfzBs=
|
||||||
github.com/yaronf/httpsign v0.3.2/go.mod h1:cYB/6toJrJnf4JTLVoo6IHzFH9/Zu1dcKmah4xfX2WA=
|
github.com/yaronf/httpsign v0.3.2/go.mod h1:cYB/6toJrJnf4JTLVoo6IHzFH9/Zu1dcKmah4xfX2WA=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||||
|
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||||
|
@ -134,6 +151,7 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
|
40
web/public/middleware/httpUpgrade.go
Normal file
40
web/public/middleware/httpUpgrade.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package webmiddleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.mstar.dev/mstar/linstrom/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AddUpgradeHeader(h http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
switch r.ProtoMajor {
|
||||||
|
case 1:
|
||||||
|
// Always offer upgrade to http2
|
||||||
|
headerText := fmt.Sprintf(
|
||||||
|
"h2=\":%d\"; ma=3600",
|
||||||
|
config.GlobalConfig.General.GetFinalPublicPort(),
|
||||||
|
)
|
||||||
|
// Offer upgrade to http3 if enabled
|
||||||
|
if config.GlobalConfig.Experimental.Http3Support && config.GlobalConfig.SSL.HandleSSL {
|
||||||
|
headerText = fmt.Sprintf(
|
||||||
|
"h3=\":%d\"; ma=3600, %s",
|
||||||
|
config.GlobalConfig.General.GetFinalPublicPort(),
|
||||||
|
headerText,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
w.Header().Add("Alt-Svc", headerText)
|
||||||
|
case 2:
|
||||||
|
// Offer upgrade to http3 if enabled
|
||||||
|
if config.GlobalConfig.Experimental.Http3Support && config.GlobalConfig.SSL.HandleSSL {
|
||||||
|
headerText := fmt.Sprintf(
|
||||||
|
"h3=\":%d\"; ma=3600",
|
||||||
|
config.GlobalConfig.General.GetFinalPublicPort(),
|
||||||
|
)
|
||||||
|
w.Header().Add("Alt-Svc", headerText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
|
@ -32,13 +32,18 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
webutils "git.mstar.dev/mstar/goutils/http"
|
webutils "git.mstar.dev/mstar/goutils/http"
|
||||||
|
"github.com/quic-go/quic-go/http3"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
|
"git.mstar.dev/mstar/linstrom/auth-new"
|
||||||
|
"git.mstar.dev/mstar/linstrom/config"
|
||||||
"git.mstar.dev/mstar/linstrom/web/public/api"
|
"git.mstar.dev/mstar/linstrom/web/public/api"
|
||||||
webmiddleware "git.mstar.dev/mstar/linstrom/web/public/middleware"
|
webmiddleware "git.mstar.dev/mstar/linstrom/web/public/middleware"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
server *http.Server
|
server *http.Server
|
||||||
|
quicServer *http3.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(addr string, duckImg *string, duckFs fs.FS) *Server {
|
func New(addr string, duckImg *string, duckFs fs.FS) *Server {
|
||||||
|
@ -51,8 +56,7 @@ func New(addr string, duckImg *string, duckFs fs.FS) *Server {
|
||||||
handler.HandleFunc("GET /errors/{name}", errorTypeHandler)
|
handler.HandleFunc("GET /errors/{name}", errorTypeHandler)
|
||||||
handler.HandleFunc("GET /default-image", buildServeDefaultImage(duckImg, duckFs))
|
handler.HandleFunc("GET /default-image", buildServeDefaultImage(duckImg, duckFs))
|
||||||
handler.HandleFunc("GET /default-image.webp", buildServeDefaultImage(duckImg, duckFs))
|
handler.HandleFunc("GET /default-image.webp", buildServeDefaultImage(duckImg, duckFs))
|
||||||
server := http.Server{
|
rootHandler := webutils.ChainMiddlewares(
|
||||||
Handler: webutils.ChainMiddlewares(
|
|
||||||
handler,
|
handler,
|
||||||
webutils.BuildLoggingMiddleware(
|
webutils.BuildLoggingMiddleware(
|
||||||
true,
|
true,
|
||||||
|
@ -61,19 +65,84 @@ func New(addr string, duckImg *string, duckFs fs.FS) *Server {
|
||||||
),
|
),
|
||||||
webmiddleware.AppendFullPathMiddleware,
|
webmiddleware.AppendFullPathMiddleware,
|
||||||
webmiddleware.TraceRequestInfoMiddleware,
|
webmiddleware.TraceRequestInfoMiddleware,
|
||||||
),
|
webmiddleware.AddUpgradeHeader,
|
||||||
|
)
|
||||||
|
server := http.Server{
|
||||||
|
Handler: rootHandler,
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
}
|
}
|
||||||
return &Server{&server}
|
quicServer := http3.Server{
|
||||||
|
Handler: rootHandler,
|
||||||
|
Addr: addr,
|
||||||
|
}
|
||||||
|
if config.GlobalConfig.General.PublicPort != nil {
|
||||||
|
quicServer.Port = *config.GlobalConfig.General.PublicPort
|
||||||
|
}
|
||||||
|
return &Server{server: &server, quicServer: &quicServer}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Start() error {
|
func (s *Server) Start() error {
|
||||||
|
if config.GlobalConfig.SSL.HandleSSL {
|
||||||
|
certFile, keyFile, err := auth.TlsFromConfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if config.GlobalConfig.Experimental.Http3Support {
|
||||||
|
errChan := make(chan error, 2)
|
||||||
|
go func() {
|
||||||
|
errChan <- s.server.ListenAndServeTLS(certFile, keyFile)
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
errChan <- s.quicServer.ListenAndServeTLS(certFile, keyFile)
|
||||||
|
}()
|
||||||
|
|
||||||
|
err := <-errChan
|
||||||
|
if err != nil && err != http.ErrServerClosed {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Always gets two values back on the channel since both servers are running
|
||||||
|
err = <-errChan
|
||||||
|
if err != nil && err != http.ErrServerClosed {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return s.server.ListenAndServeTLS(certFile, keyFile)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// But refuse http3 support here, since it doesn't work without tls
|
||||||
return s.server.ListenAndServe()
|
return s.server.ListenAndServe()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) Stop() error {
|
func (s *Server) Stop() error {
|
||||||
|
if config.GlobalConfig.SSL.HandleSSL && config.GlobalConfig.Experimental.Http3Support {
|
||||||
|
errChan := make(chan error, 2)
|
||||||
|
go func() {
|
||||||
|
log.Debug().Msg("Stopping tcp server")
|
||||||
|
errChan <- s.server.Shutdown(context.Background())
|
||||||
|
log.Debug().Msg("Tcp server stopped")
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
log.Debug().Msg("Stopping udp server")
|
||||||
|
errChan <- s.quicServer.Shutdown(context.Background())
|
||||||
|
log.Debug().Msg("Udp server stopped")
|
||||||
|
}()
|
||||||
|
|
||||||
|
err := <-errChan
|
||||||
|
if err != nil && err != http.ErrServerClosed {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Always gets two values back on the channel since both servers are running
|
||||||
|
err = <-errChan
|
||||||
|
if err != nil && err != http.ErrServerClosed {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
return s.server.Shutdown(context.Background())
|
return s.server.Shutdown(context.Background())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func buildServeDefaultImage(
|
func buildServeDefaultImage(
|
||||||
duckImg *string,
|
duckImg *string,
|
||||||
|
|
Loading…
Reference in a new issue