traefik/acme/localStore.go

195 lines
4.9 KiB
Go
Raw Normal View History

package acme
import (
"encoding/json"
"io/ioutil"
"os"
2018-03-26 12:12:03 +00:00
"regexp"
"github.com/containous/traefik/log"
2018-03-05 19:54:04 +00:00
"github.com/containous/traefik/provider/acme"
)
// LocalStore is a store using a file as storage
type LocalStore struct {
2018-03-05 19:54:04 +00:00
file string
}
// NewLocalStore create a LocalStore
func NewLocalStore(file string) *LocalStore {
return &LocalStore{
file: file,
}
}
2018-03-05 19:54:04 +00:00
// Get loads file into store and returns the Account
func (s *LocalStore) Get() (*Account, error) {
account := &Account{}
2018-04-10 08:52:04 +00:00
hasData, err := acme.CheckFile(s.file)
if err != nil {
return nil, err
}
2018-03-05 19:54:04 +00:00
if hasData {
f, err := os.Open(s.file)
if err != nil {
return nil, err
}
defer f.Close()
file, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
if err := json.Unmarshal(file, &account); err != nil {
return nil, err
}
}
2018-03-26 12:12:03 +00:00
return account, nil
}
2018-03-26 12:12:03 +00:00
// RemoveAccountV1Values removes ACME account V1 values
func RemoveAccountV1Values(account *Account) error {
// Check if ACME Account is in ACME V1 format
if account != nil && account.Registration != nil {
isOldRegistration, err := regexp.MatchString(acme.RegistrationURLPathV1Regexp, account.Registration.URI)
if err != nil {
return err
2018-03-26 12:12:03 +00:00
}
if isOldRegistration {
account.Email = ""
account.Registration = nil
account.PrivateKey = nil
}
}
return nil
}
2018-03-05 19:54:04 +00:00
// ConvertToNewFormat converts old acme.json format to the new one and store the result into the file (used for the backward compatibility)
func ConvertToNewFormat(fileName string) {
localStore := acme.NewLocalStore(fileName)
2018-03-26 12:12:03 +00:00
2018-03-05 19:54:04 +00:00
storeAccount, err := localStore.GetAccount()
if err != nil {
log.Errorf("Failed to read new account, ACME data conversion is not available : %v", err)
2018-03-05 19:54:04 +00:00
return
}
2018-03-26 12:12:03 +00:00
storeCertificates, err := localStore.GetCertificates()
if err != nil {
log.Errorf("Failed to read new certificates, ACME data conversion is not available : %v", err)
2018-03-26 12:12:03 +00:00
return
}
2018-03-05 19:54:04 +00:00
if storeAccount == nil {
localStore := NewLocalStore(fileName)
2018-03-05 19:54:04 +00:00
account, err := localStore.Get()
if err != nil {
log.Errorf("Failed to read old account, ACME data conversion is not available : %v", err)
2018-03-05 19:54:04 +00:00
return
}
2018-03-26 12:12:03 +00:00
// Convert ACME data from old to new format
newAccount := &acme.Account{}
if account != nil && len(account.Email) > 0 {
err = backupACMEFile(fileName, account)
if err != nil {
log.Errorf("Unable to create a backup for the V1 formatted ACME file: %s", err.Error())
return
}
err = RemoveAccountV1Values(account)
if err != nil {
log.Errorf("Unable to remove ACME Account V1 values: %s", err.Error())
return
}
2018-03-26 12:12:03 +00:00
newAccount = &acme.Account{
2018-03-05 19:54:04 +00:00
PrivateKey: account.PrivateKey,
Registration: account.Registration,
Email: account.Email,
}
var newCertificates []*acme.Certificate
for _, cert := range account.DomainsCertificate.Certs {
newCertificates = append(newCertificates, &acme.Certificate{
Certificate: cert.Certificate.Certificate,
Key: cert.Certificate.PrivateKey,
Domain: cert.Domains,
})
}
// If account is in the old format, storeCertificates is nil or empty and has to be initialized
2018-03-26 12:12:03 +00:00
storeCertificates = newCertificates
2018-03-05 19:54:04 +00:00
}
2018-03-26 12:12:03 +00:00
// Store the data in new format into the file even if account is nil
// to delete Account in ACME v1 format and keeping the certificates
newLocalStore := acme.NewLocalStore(fileName)
newLocalStore.SaveDataChan <- &acme.StoredData{Account: newAccount, Certificates: storeCertificates}
}
2018-03-05 19:54:04 +00:00
}
func backupACMEFile(originalFileName string, account interface{}) error {
// write account to file
data, err := json.MarshalIndent(account, "", " ")
if err != nil {
return err
}
return ioutil.WriteFile(originalFileName+".bak", data, 0600)
}
// FromNewToOldFormat converts new acme account to the old one (used for the backward compatibility)
2018-03-05 19:54:04 +00:00
func FromNewToOldFormat(fileName string) (*Account, error) {
localStore := acme.NewLocalStore(fileName)
2018-03-05 19:54:04 +00:00
storeAccount, err := localStore.GetAccount()
if err != nil {
2018-03-05 19:54:04 +00:00
return nil, err
}
2018-03-05 19:54:04 +00:00
storeCertificates, err := localStore.GetCertificates()
if err != nil {
2018-03-05 19:54:04 +00:00
return nil, err
}
2018-03-26 12:12:03 +00:00
// Convert ACME Account from new to old format
// (Needed by the KV stores)
var account *Account
2018-03-05 19:54:04 +00:00
if storeAccount != nil {
2018-03-26 12:12:03 +00:00
account = &Account{
Email: storeAccount.Email,
PrivateKey: storeAccount.PrivateKey,
Registration: storeAccount.Registration,
DomainsCertificate: DomainsCertificates{},
}
}
2018-03-05 19:54:04 +00:00
2018-03-26 12:12:03 +00:00
// Convert ACME Certificates from new to old format
// (Needed by the KV stores)
if len(storeCertificates) > 0 {
// Account can be nil if data are migrated from new format
// with a ACME V1 Account
if account == nil {
account = &Account{}
}
2018-03-05 19:54:04 +00:00
for _, cert := range storeCertificates {
2018-03-26 12:12:03 +00:00
_, err := account.DomainsCertificate.addCertificateForDomains(&Certificate{
2018-03-05 19:54:04 +00:00
Domain: cert.Domain.Main,
Certificate: cert.Certificate,
PrivateKey: cert.Key,
}, cert.Domain)
if err != nil {
return nil, err
}
}
}
2018-03-26 12:12:03 +00:00
return account, nil
}