mirror of
https://codeberg.org/Codeberg/pages-server.git
synced 2024-11-05 14:07:01 +00:00
7e80ade24b
Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/263 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: crapStone <me@crapstone.dev> Co-committed-by: crapStone <me@crapstone.dev>
110 lines
3.4 KiB
Go
110 lines
3.4 KiB
Go
package certificates
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
|
|
"codeberg.org/codeberg/pages/config"
|
|
"github.com/go-acme/lego/v4/certcrypto"
|
|
"github.com/go-acme/lego/v4/lego"
|
|
"github.com/go-acme/lego/v4/registration"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
const challengePath = "/.well-known/acme-challenge/"
|
|
|
|
func setupAcmeConfig(cfg config.ACMEConfig) (*lego.Config, error) {
|
|
var myAcmeAccount AcmeAccount
|
|
var myAcmeConfig *lego.Config
|
|
|
|
if cfg.AccountConfigFile == "" {
|
|
return nil, fmt.Errorf("invalid acme config file: '%s'", cfg.AccountConfigFile)
|
|
}
|
|
|
|
if account, err := os.ReadFile(cfg.AccountConfigFile); err == nil {
|
|
log.Info().Msgf("found existing acme account config file '%s'", cfg.AccountConfigFile)
|
|
if err := json.Unmarshal(account, &myAcmeAccount); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
myAcmeAccount.Key, err = certcrypto.ParsePEMPrivateKey([]byte(myAcmeAccount.KeyPEM))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
myAcmeConfig = lego.NewConfig(&myAcmeAccount)
|
|
myAcmeConfig.CADirURL = cfg.APIEndpoint
|
|
myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048
|
|
|
|
// Validate Config
|
|
_, err := lego.NewClient(myAcmeConfig)
|
|
if err != nil {
|
|
log.Info().Err(err).Msg("config validation failed, you might just delete the config file and let it recreate")
|
|
return nil, fmt.Errorf("acme config validation failed: %w", err)
|
|
}
|
|
|
|
return myAcmeConfig, nil
|
|
} else if !os.IsNotExist(err) {
|
|
return nil, err
|
|
}
|
|
|
|
log.Info().Msgf("no existing acme account config found, try to create a new one")
|
|
|
|
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
myAcmeAccount = AcmeAccount{
|
|
Email: cfg.Email,
|
|
Key: privateKey,
|
|
KeyPEM: string(certcrypto.PEMEncode(privateKey)),
|
|
}
|
|
myAcmeConfig = lego.NewConfig(&myAcmeAccount)
|
|
myAcmeConfig.CADirURL = cfg.APIEndpoint
|
|
myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048
|
|
tempClient, err := lego.NewClient(myAcmeConfig)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only")
|
|
} else {
|
|
// accept terms & log in to EAB
|
|
if cfg.EAB_KID == "" || cfg.EAB_HMAC == "" {
|
|
reg, err := tempClient.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: cfg.AcceptTerms})
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Can't register ACME account, continuing with mock certs only")
|
|
} else {
|
|
myAcmeAccount.Registration = reg
|
|
}
|
|
} else {
|
|
reg, err := tempClient.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
|
|
TermsOfServiceAgreed: cfg.AcceptTerms,
|
|
Kid: cfg.EAB_KID,
|
|
HmacEncoded: cfg.EAB_HMAC,
|
|
})
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Can't register ACME account, continuing with mock certs only")
|
|
} else {
|
|
myAcmeAccount.Registration = reg
|
|
}
|
|
}
|
|
|
|
if myAcmeAccount.Registration != nil {
|
|
acmeAccountJSON, err := json.Marshal(myAcmeAccount)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("json.Marshalfailed, waiting for manual restart to avoid rate limits")
|
|
select {}
|
|
}
|
|
log.Info().Msgf("new acme account created. write to config file '%s'", cfg.AccountConfigFile)
|
|
err = os.WriteFile(cfg.AccountConfigFile, acmeAccountJSON, 0o600)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("os.WriteFile failed, waiting for manual restart to avoid rate limits")
|
|
select {}
|
|
}
|
|
}
|
|
}
|
|
|
|
return myAcmeConfig, nil
|
|
}
|