package auth import ( "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/tls" "crypto/x509" "strings" "time" "github.com/go-acme/lego/v4/certcrypto" "github.com/go-acme/lego/v4/certificate" "github.com/go-acme/lego/v4/challenge/http01" "github.com/go-acme/lego/v4/challenge/tlsalpn01" "github.com/go-acme/lego/v4/lego" "github.com/go-acme/lego/v4/registration" "git.mstar.dev/mstar/linstrom/config" "git.mstar.dev/mstar/linstrom/storage-new/dbgen" "git.mstar.dev/mstar/linstrom/storage-new/models" ) type leUser struct { Email string Registration *registration.Resource Key crypto.PrivateKey } func (l *leUser) GetEmail() string { return l.Email } func (l *leUser) GetRegistration() *registration.Resource { return l.Registration } func (l *leUser) GetPrivateKey() crypto.PrivateKey { return l.Key } func TlsFromConfig() (*tls.Config, error) { if config.GlobalConfig.SSL.UseLetsEncrypt != nil && *config.GlobalConfig.SSL.UseLetsEncrypt { return tlsFromLetsEncrypt() } panic("Not implemented") } func tlsFromLetsEncrypt() (*tls.Config, error) { metadata, err := dbgen.ServerMetadata.First() if err != nil { return nil, err } if !metadata.LELastUpdate.Valid { return generateLetsEncryptCert(metadata) } 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 // See https://letsencrypt.org/docs/faq/#what-is-the-lifetime-for-let-s-encrypt-certificates-for-how-long-are-they-valid return generateLetsEncryptCert(metadata) } panic("Not implemented") } // (Re-)Generate a Lets Encrypt certificate using the provided details in the config // Automatically stores the newly generated certificate in the db, encrypting beforehand func generateLetsEncryptCert(metadata *models.ServerMetadata) (*tls.Config, error) { // Follow https://go-acme.github.io/lego/usage/library/index.html var userPrivateKey *ecdsa.PrivateKey var err error if len(metadata.LEUserPrivKey) == 0 { userPrivateKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, err } marshalled, err := x509.MarshalECPrivateKey(userPrivateKey) if err != nil { return nil, err } encryptedKey, err := Encrypt( []byte(config.GlobalConfig.Storage.EncryptionKey), marshalled, ) if err != nil { return nil, err } metadata.LEUserPrivKey = encryptedKey } else { decryptedKey, err := Decrypt([]byte(config.GlobalConfig.Storage.EncryptionKey), metadata.LEUserPrivKey) if err != nil { return nil, err } userPrivateKey, err = x509.ParseECPrivateKey(decryptedKey) if err != nil { return nil, err } } user := leUser{Email: *config.GlobalConfig.SSL.AdminMail} leConfig := lego.NewConfig(&user) leConfig.CADirURL = strings.Replace( config.GlobalConfig.General.GetFullPublicUrl(), "https", "http", 1, ) + "/directory" leConfig.Certificate.KeyType = certcrypto.RSA2048 leClient, err := lego.NewClient(leConfig) if err != nil { return nil, err } // TODO: Figure out these servers err = leClient.Challenge.SetHTTP01Provider(http01.NewProviderServer("", "5002")) if err != nil { return nil, err } err = leClient.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", "5001")) if err != nil { return nil, err } reg, err := leClient.Registration.Register( registration.RegisterOptions{TermsOfServiceAgreed: true}, ) if err != nil { return nil, err } user.Registration = reg req := certificate.ObtainRequest{ Domains: []string{config.GlobalConfig.General.GetFullDomain()}, Bundle: true, } certificates, err := leClient.Certificate.Obtain(req) if err != nil { return nil, err } _ = certificates // TODO: Do something with certificates here panic("Not implemented") }