chore: Restructure

This commit is contained in:
2024-01-01 14:19:19 +01:00
parent 85bd71cff3
commit 3af3f6bb7e
14 changed files with 457 additions and 441 deletions

View File

@@ -0,0 +1,109 @@
package certificates
import (
"crypto/rsa"
"crypto/tls"
"encoding/base64"
"encoding/pem"
"time"
"bytes"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"math/big"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/lego"
)
func ObtainNewCertificate(domains []string, acmeClient *lego.Client) (CertificateWrapper, error) {
req := certificate.ObtainRequest{
Domains: domains,
Bundle: true,
}
cert, err := acmeClient.Certificate.Obtain(req)
if err != nil {
return CertificateWrapper{}, err
}
tlsCert, err := tls.X509KeyPair(cert.Certificate, cert.PrivateKey)
if err != nil {
return CertificateWrapper{}, err
}
wrapper := CertificateWrapper{
TlsCertificate: &tlsCert,
Domain: cert.Domain,
//NotAfter: tlsCert.Leaf.NotAfter,
NotAfter: time.Now().Add(time.Hour * 24 * 60),
PrivateKeyEncoded: base64.StdEncoding.EncodeToString(cert.PrivateKey),
Certificate: cert.Certificate,
IssuerCertificate: cert.IssuerCertificate,
CertificateUrl: cert.CertURL,
}
return wrapper, nil
}
// Generate a fallback certificate for the domain.
func MakeFallbackCertificate(pagesDomain string) (*CertificateWrapper, error) {
key, err := certcrypto.GeneratePrivateKey(certcrypto.RSA2048)
if err != nil {
return nil, err
}
notAfter := time.Now().Add(time.Hour * 24 * 7)
cert := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
CommonName: pagesDomain,
Organization: []string{"Pages Server"},
},
NotAfter: notAfter,
NotBefore: time.Now(),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
certBytes, err := x509.CreateCertificate(
rand.Reader,
&cert,
&cert,
&key.(*rsa.PrivateKey).PublicKey,
key,
)
if err != nil {
return nil, err
}
out := &bytes.Buffer{}
err = pem.Encode(out, &pem.Block{
Bytes: certBytes,
Type: "CERTIFICATE",
})
if err != nil {
return nil, err
}
outBytes := out.Bytes()
res := &certificate.Resource{
PrivateKey: certcrypto.PEMEncode(key),
Certificate: outBytes,
IssuerCertificate: outBytes,
Domain: pagesDomain,
}
tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey)
if err != nil {
return nil, err
}
return &CertificateWrapper{
TlsCertificate: &tlsCertificate,
Domain: pagesDomain,
NotAfter: notAfter,
PrivateKeyEncoded: base64.StdEncoding.EncodeToString(certcrypto.PEMEncode(key)),
Certificate: outBytes,
IssuerCertificate: outBytes,
CertificateUrl: "localhost",
}, nil
}

View File

@@ -0,0 +1,114 @@
package certificates
import (
"crypto/rsa"
"crypto/tls"
"encoding/base64"
"encoding/json"
"io/ioutil"
"time"
"github.com/go-acme/lego/v4/certcrypto"
log "github.com/sirupsen/logrus"
)
// A convenience wrapper around a TLS certificate
type CertificateWrapper struct {
TlsCertificate *tls.Certificate `json:"-"`
Domain string `json:"domain"`
NotAfter time.Time `json:"not_after"`
PrivateKeyEncoded string `json:"private_key"`
Certificate []byte `json:"certificate"`
IssuerCertificate []byte `json:"issuer_certificate"`
CertificateUrl string `json:"certificate_url"`
}
// A structure to store all the certificates we know of in.
type CertificatesCache struct {
// The certificate to use as a fallback if all else fails.
FallbackCertificate *CertificateWrapper
// Mapping of domain name to certificate.
Certificates map[string]CertificateWrapper
}
// Internal type to let encoding JSON handle the bulk of the work.
type certificatesStore struct {
FallbackCertificate CertificateWrapper `json:"fallback"`
Certificates []CertificateWrapper `json:"certificates"`
}
// Decodes the private key of the certificate wrapper.
func (c *CertificateWrapper) GetPrivateKey() *rsa.PrivateKey {
data, _ := base64.StdEncoding.DecodeString(c.PrivateKeyEncoded)
pk, _ := certcrypto.ParsePEMPrivateKey(data)
return pk.(*rsa.PrivateKey)
}
// Populate the certificate's TlsCertificate field.
func (c *CertificateWrapper) initTlsCertificate() {
pk, _ := base64.StdEncoding.DecodeString(c.PrivateKeyEncoded)
tlsCert, _ := tls.X509KeyPair(
c.Certificate,
pk,
)
c.TlsCertificate = &tlsCert
}
// Checks if the certificate is still valid now.
func (c *CertificateWrapper) IsValid() bool {
return time.Now().Compare(c.NotAfter) <= -1
}
// Serializes the certificate cache to a JSON string for writing to a file.
func (c *CertificatesCache) toStoreData() []byte {
certs := make([]CertificateWrapper, 0)
for _, cert := range c.Certificates {
certs = append(certs, cert)
}
result, err := json.Marshal(certificatesStore{
FallbackCertificate: *c.FallbackCertificate,
Certificates: certs,
})
if err != nil {
log.Errorf("Failed to Marshal cache: %v", err)
}
return result
}
// Saves the cache to disk.
func (c *CertificatesCache) FlushToDisk(path string) {
ioutil.WriteFile(path, c.toStoreData(), 0600)
}
func (c *CertificatesCache) AddCert(cert CertificateWrapper, path string) {
c.Certificates[cert.Domain] = cert
c.FlushToDisk(path)
}
// Load the certificate cache from the file.
func CertificateCacheFromFile(path string) (CertificatesCache, error) {
content, err := ioutil.ReadFile(path)
if err != nil {
return CertificatesCache{}, err
}
var store certificatesStore
_ = json.Unmarshal(content, &store)
store.FallbackCertificate.initTlsCertificate()
cache := CertificatesCache{
FallbackCertificate: &store.FallbackCertificate,
}
certs := make(map[string]CertificateWrapper)
for _, cert := range store.Certificates {
cert.initTlsCertificate()
certs[cert.Domain] = cert
}
cache.Certificates = certs
return cache, nil
}