Follow-up from #639. At the moment people that were affected by this security issue would still be vulnerable even after upgrading. This patch makes sure permissions are also checked for already existing files. Signed-off-by: Bilal Amarni <bilal.amarni@gmail.com>
101 lines
2.1 KiB
Go
101 lines
2.1 KiB
Go
package acme
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"sync"
|
|
|
|
"github.com/containous/traefik/cluster"
|
|
"github.com/containous/traefik/log"
|
|
)
|
|
|
|
var _ cluster.Store = (*LocalStore)(nil)
|
|
|
|
// LocalStore is a store using a file as storage
|
|
type LocalStore struct {
|
|
file string
|
|
storageLock sync.RWMutex
|
|
account *Account
|
|
}
|
|
|
|
// NewLocalStore create a LocalStore
|
|
func NewLocalStore(file string) *LocalStore {
|
|
return &LocalStore{
|
|
file: file,
|
|
}
|
|
}
|
|
|
|
// Get atomically a struct from the file storage
|
|
func (s *LocalStore) Get() cluster.Object {
|
|
s.storageLock.RLock()
|
|
defer s.storageLock.RUnlock()
|
|
return s.account
|
|
}
|
|
|
|
// Load loads file into store
|
|
func (s *LocalStore) Load() (cluster.Object, error) {
|
|
s.storageLock.Lock()
|
|
defer s.storageLock.Unlock()
|
|
account := &Account{}
|
|
|
|
f, err := os.Open(s.file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
fi, err := f.Stat()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if fi.Mode().Perm()&0077 != 0 {
|
|
return nil, fmt.Errorf("permissions %o for %s are too open, please use 600", fi.Mode().Perm(), s.file)
|
|
}
|
|
|
|
file, err := ioutil.ReadAll(f)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := json.Unmarshal(file, &account); err != nil {
|
|
return nil, err
|
|
}
|
|
account.Init()
|
|
s.account = account
|
|
log.Infof("Loaded ACME config from store %s", s.file)
|
|
return account, nil
|
|
}
|
|
|
|
// Begin creates a transaction with the KV store.
|
|
func (s *LocalStore) Begin() (cluster.Transaction, cluster.Object, error) {
|
|
s.storageLock.Lock()
|
|
return &localTransaction{LocalStore: s}, s.account, nil
|
|
}
|
|
|
|
var _ cluster.Transaction = (*localTransaction)(nil)
|
|
|
|
type localTransaction struct {
|
|
*LocalStore
|
|
dirty bool
|
|
}
|
|
|
|
// Commit allows to set an object in the file storage
|
|
func (t *localTransaction) Commit(object cluster.Object) error {
|
|
t.LocalStore.account = object.(*Account)
|
|
defer t.storageLock.Unlock()
|
|
if t.dirty {
|
|
return fmt.Errorf("transaction already used, please begin a new one")
|
|
}
|
|
|
|
// write account to file
|
|
data, err := json.MarshalIndent(object, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = ioutil.WriteFile(t.file, data, 0600)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
t.dirty = true
|
|
return nil
|
|
}
|