2018-03-05 20:54:04 +01:00
package acme
import (
"encoding/json"
2021-03-04 20:08:03 +01:00
"io"
2018-03-05 20:54:04 +01:00
"os"
2018-07-03 12:44:04 +02:00
"sync"
2018-03-05 20:54:04 +01:00
2020-09-16 15:46:04 +02:00
"github.com/traefik/traefik/v2/pkg/log"
"github.com/traefik/traefik/v2/pkg/safe"
2018-03-05 20:54:04 +01:00
)
var _ Store = ( * LocalStore ) ( nil )
2020-05-11 12:06:07 +02:00
// LocalStore Stores implementation for local file.
2018-03-05 20:54:04 +01:00
type LocalStore struct {
2019-07-19 11:52:04 +02:00
saveDataChan chan map [ string ] * StoredData
2018-03-05 20:54:04 +01:00
filename string
2019-07-19 11:52:04 +02:00
lock sync . RWMutex
storedData map [ string ] * StoredData
2018-03-05 20:54:04 +01:00
}
2020-05-11 12:06:07 +02:00
// NewLocalStore initializes a new LocalStore with a file name.
2018-07-03 12:44:04 +02:00
func NewLocalStore ( filename string ) * LocalStore {
2019-07-19 11:52:04 +02:00
store := & LocalStore { filename : filename , saveDataChan : make ( chan map [ string ] * StoredData ) }
2018-03-05 20:54:04 +01:00
store . listenSaveAction ( )
return store
}
2019-07-19 11:52:04 +02:00
func ( s * LocalStore ) save ( resolverName string , storedData * StoredData ) {
s . lock . Lock ( )
defer s . lock . Unlock ( )
s . storedData [ resolverName ] = storedData
2020-09-30 13:04:04 +03:00
// we cannot pass s.storedData directly, map is reference type and as result
// we can face with race condition, so we need to work with objects copy
s . saveDataChan <- s . unSafeCopyOfStoredData ( )
2019-07-19 11:52:04 +02:00
}
func ( s * LocalStore ) get ( resolverName string ) ( * StoredData , error ) {
s . lock . Lock ( )
defer s . lock . Unlock ( )
2018-03-05 20:54:04 +01:00
if s . storedData == nil {
2019-07-19 11:52:04 +02:00
s . storedData = map [ string ] * StoredData { }
2018-03-05 20:54:04 +01:00
2018-04-10 10:52:04 +02:00
hasData , err := CheckFile ( s . filename )
2018-03-05 20:54:04 +01:00
if err != nil {
return nil , err
}
2018-04-10 10:52:04 +02:00
if hasData {
2018-11-14 10:18:03 +01:00
logger := log . WithoutContext ( ) . WithField ( log . ProviderName , "acme" )
2018-04-10 10:52:04 +02:00
f , err := os . Open ( s . filename )
if err != nil {
2018-03-05 20:54:04 +01:00
return nil , err
}
2018-04-10 10:52:04 +02:00
defer f . Close ( )
2021-03-04 20:08:03 +01:00
file , err := io . ReadAll ( f )
2018-03-26 14:12:03 +02:00
if err != nil {
return nil , err
}
2018-04-10 10:52:04 +02:00
if len ( file ) > 0 {
2019-07-19 11:52:04 +02:00
if err := json . Unmarshal ( file , & s . storedData ) ; err != nil {
2018-04-10 10:52:04 +02:00
return nil , err
}
}
2018-04-16 19:34:04 +02:00
// Delete all certificates with no value
2019-07-19 11:52:04 +02:00
var certificates [ ] * CertAndStore
for _ , storedData := range s . storedData {
for _ , certificate := range storedData . Certificates {
if len ( certificate . Certificate . Certificate ) == 0 || len ( certificate . Key ) == 0 {
logger . Debugf ( "Deleting empty certificate %v for %v" , certificate , certificate . Domain . ToStrArray ( ) )
continue
}
certificates = append ( certificates , certificate )
}
if len ( certificates ) < len ( storedData . Certificates ) {
storedData . Certificates = certificates
2020-09-30 13:04:04 +03:00
// we cannot pass s.storedData directly, map is reference type and as result
// we can face with race condition, so we need to work with objects copy
s . saveDataChan <- s . unSafeCopyOfStoredData ( )
2018-04-16 19:34:04 +02:00
}
}
2018-03-26 14:12:03 +02:00
}
2018-03-05 20:54:04 +01:00
}
2019-07-19 11:52:04 +02:00
if s . storedData [ resolverName ] == nil {
s . storedData [ resolverName ] = & StoredData { }
}
return s . storedData [ resolverName ] , nil
2018-03-05 20:54:04 +01:00
}
2020-05-11 12:06:07 +02:00
// listenSaveAction listens to a chan to store ACME data in json format into `LocalStore.filename`.
2018-03-05 20:54:04 +01:00
func ( s * LocalStore ) listenSaveAction ( ) {
safe . Go ( func ( ) {
2018-11-14 10:18:03 +01:00
logger := log . WithoutContext ( ) . WithField ( log . ProviderName , "acme" )
2019-07-19 11:52:04 +02:00
for object := range s . saveDataChan {
2018-03-05 20:54:04 +01:00
data , err := json . MarshalIndent ( object , "" , " " )
if err != nil {
2018-11-14 10:18:03 +01:00
logger . Error ( err )
2018-03-05 20:54:04 +01:00
}
2021-03-04 20:08:03 +01:00
err = os . WriteFile ( s . filename , data , 0 o600 )
2018-03-05 20:54:04 +01:00
if err != nil {
2018-11-14 10:18:03 +01:00
logger . Error ( err )
2018-03-05 20:54:04 +01:00
}
}
} )
}
2020-09-30 13:04:04 +03:00
// unSafeCopyOfStoredData creates maps copy of storedData. Is not thread safe, you should use `s.lock`.
func ( s * LocalStore ) unSafeCopyOfStoredData ( ) map [ string ] * StoredData {
result := map [ string ] * StoredData { }
for k , v := range s . storedData {
result [ k ] = v
}
return result
}
2020-05-11 12:06:07 +02:00
// GetAccount returns ACME Account.
2019-07-19 11:52:04 +02:00
func ( s * LocalStore ) GetAccount ( resolverName string ) ( * Account , error ) {
storedData , err := s . get ( resolverName )
2018-03-05 20:54:04 +01:00
if err != nil {
return nil , err
}
return storedData . Account , nil
}
2020-05-11 12:06:07 +02:00
// SaveAccount stores ACME Account.
2019-07-19 11:52:04 +02:00
func ( s * LocalStore ) SaveAccount ( resolverName string , account * Account ) error {
storedData , err := s . get ( resolverName )
2018-03-05 20:54:04 +01:00
if err != nil {
return err
}
storedData . Account = account
2019-07-19 11:52:04 +02:00
s . save ( resolverName , storedData )
2018-03-05 20:54:04 +01:00
return nil
}
2020-05-11 12:06:07 +02:00
// GetCertificates returns ACME Certificates list.
2019-07-19 11:52:04 +02:00
func ( s * LocalStore ) GetCertificates ( resolverName string ) ( [ ] * CertAndStore , error ) {
storedData , err := s . get ( resolverName )
2018-03-05 20:54:04 +01:00
if err != nil {
return nil , err
}
return storedData . Certificates , nil
}
2020-05-11 12:06:07 +02:00
// SaveCertificates stores ACME Certificates list.
2019-07-19 11:52:04 +02:00
func ( s * LocalStore ) SaveCertificates ( resolverName string , certificates [ ] * CertAndStore ) error {
storedData , err := s . get ( resolverName )
2018-03-05 20:54:04 +01:00
if err != nil {
return err
}
storedData . Certificates = certificates
2019-07-19 11:52:04 +02:00
s . save ( resolverName , storedData )
2018-03-05 20:54:04 +01:00
return nil
}