From bf6021c8bb34394a70ed49c7e816b0aee4140992 Mon Sep 17 00:00:00 2001 From: Artur Marud Date: Mon, 27 Jun 2022 17:47:07 +0200 Subject: [PATCH] sshfp-generator: New features and fixes * Support for deleted hosts (cleanup records after configured time) * Small fixes Signed-off-by: Artur Marud --- README.md | 3 ++- cloudflare/interface.go | 1 + cloudflare/service.go | 17 +++++++++++++ config/entity.go | 4 ++- config/service.go | 25 +++++++++++++----- main.go | 22 ++++++++++++++-- statestore/interface.go | 7 +++++ statestore/mapRepository.go | 51 ++++++++++++++++++++++++++++++++++--- statestore/service.go | 26 ++++++++++++++++++- 9 files changed, 140 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index a497e56..ec39758 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,8 @@ go build -mod vendor Supported env variables: `DOMAIN_NAME` - Domain name which will be working on `CF_TOKEN` - CloudFlare Token with write access to above domain -`CONSUL_TOKEN` - Consul Token with priviledges to read services +`HOST_LIVENESS_TIMEOUT` - number in seconds after which host is +considered as removed and dns records can be deleted It's possible to create json formatted config file (example in `testcfg`) diff --git a/cloudflare/interface.go b/cloudflare/interface.go index 43d42a2..65f7e19 100644 --- a/cloudflare/interface.go +++ b/cloudflare/interface.go @@ -16,6 +16,7 @@ type Repository interface { type Service interface { FindHostByName(hostname string) (bool, error) GetSSHFPRecordsForHost(hostname string) ([]*sshfp.SSHFPRecord, error) + DeleteSSHFPRecordsForHost(hostname string) error CreateSSHFPRecord(hostname string, record sshfp.SSHFPRecord) (int, error) DeleteSSHFPRecord(hostname string, record sshfp.SSHFPRecord) error UpdateSSHFPRecord(hostname string, record sshfp.SSHFPRecord) error diff --git a/cloudflare/service.go b/cloudflare/service.go index 546ac75..5f470cd 100644 --- a/cloudflare/service.go +++ b/cloudflare/service.go @@ -63,6 +63,23 @@ func (s *service) GetSSHFPRecordsForHost(hostname string) ([]*sshfp.SSHFPRecord, return output, nil } +func (s *service) DeleteSSHFPRecordsForHost(hostname string) error { + logrus.Debugf("cloudflare: DeleteSSHFPRecordsForHost: %s", hostname) + records, err := s.GetSSHFPRecordsForHost(hostname) + if err != nil { + return err + } + + for _, record := range records { + err := s.DeleteSSHFPRecord(hostname, *record) + if err != nil { + return nil + } + } + return nil + +} + func (s *service) CreateSSHFPRecord(hostname string, record sshfp.SSHFPRecord) (int, error) { logrus.Infof("cloudflare: CreateSSHFPRecord: %+v", record) diff --git a/config/entity.go b/config/entity.go index 4e55dcb..6b78284 100644 --- a/config/entity.go +++ b/config/entity.go @@ -1,7 +1,9 @@ package config type Config struct { - ConsulToken string `json:"consulKey"` + //ConsulToken string `json:"consulKey"` CloudflareToken string `json:"cloudflareKey"` DomainName string `json:"domain"` + HostTimeout int `json:"hostLivenessTimeout"` + LogLevel string `json:"logLevel"` } diff --git a/config/service.go b/config/service.go index 8681d19..122cf06 100644 --- a/config/service.go +++ b/config/service.go @@ -3,6 +3,7 @@ package config import ( "errors" "os" + "strconv" "github.com/sirupsen/logrus" ) @@ -22,17 +23,27 @@ func (s *service) LoadConfig(fileName string) (*Config, error) { if !exists { return nil, errors.New("cannot find env variable CF_TOKEN") } - consulToken, exists := os.LookupEnv("CONSUL_TOKEN") - if !exists { - return nil, errors.New("cannot find env variable CONSUL_TOKEN") - } - domainaName, exists := os.LookupEnv("DOMAIN_NAME") - if exists { + if !exists { return nil, errors.New("cannot find env variable DOMAIN_NAME") } - return &Config{ConsulToken: consulToken, CloudflareToken: cfToken, DomainName: domainaName}, nil + hostTimeout, exists := os.LookupEnv("HOST_LIVENESS_TIMEOUT") + if !exists { + return nil, errors.New("cannot find env variable HOST_LIVENESS_TIMEOUT") + } + hostTimeoutInt, err := strconv.ParseInt(hostTimeout, 10, 32) + if err != nil { + return nil, errors.New("incorrect HOST_LIVENESS_TIMEOUT value") + + } + + logLevel, exists := os.LookupEnv(("LOG_LEVEL")) + if !exists { + return nil, errors.New("cannot find env variable LOG_LEVEL") + } + + return &Config{CloudflareToken: cfToken, DomainName: domainaName, HostTimeout: int(hostTimeoutInt), LogLevel: logLevel}, nil } diff --git a/main.go b/main.go index e8998a7..01e486c 100644 --- a/main.go +++ b/main.go @@ -6,17 +6,24 @@ import ( "infra-sshfp-cf/consul" "infra-sshfp-cf/sshfp" "infra-sshfp-cf/statestore" + "os" "github.com/sirupsen/logrus" ) func main() { //Debug loglevel - logrus.SetLevel(logrus.InfoLevel) + logrus.SetLevel(logrus.DebugLevel) //Create configuration components + // Get config file name from args, if empty - try to configure from ENVs + var configFilename string = "" + if len(os.Args) > 1 { + configFilename = os.Args[1] + } + cfgService := config.NewService(config.NewFileRepository()) - config, err := cfgService.LoadConfig("testcfg") + config, err := cfgService.LoadConfig(configFilename) if err != nil { logrus.Fatal(err) } @@ -81,4 +88,15 @@ func main() { } } + //Purge old hosts + hosts, _ = statestore.GetStalledHosts(config.HostTimeout) + for _, host := range hosts { + err := cloudflare.DeleteSSHFPRecordsForHost(host) + if err != nil { + logrus.Fatalf("Cannot delete records for host: %s", host) + } + + } + statestore.PurgeStalledHosts(config.HostTimeout) + } diff --git a/statestore/interface.go b/statestore/interface.go index 4013679..4ec9ff7 100644 --- a/statestore/interface.go +++ b/statestore/interface.go @@ -1,11 +1,18 @@ package statestore +import "time" + type Repository interface { GetModifyIndex(hostname string) (int, error) SetModifyIndex(hostname string, index int) error + DeleteHost(hostname string) error + DeleteHosts(hostnames []string) error + GetOutdatedHosts(time time.Duration) ([]string, error) } type Service interface { CheckIfModified(hostname string, index int) (bool, error) SaveState(hostname string, index int) error + PurgeStalledHosts(timeTreshold int) error + GetStalledHosts(timeTreshold int) ([]string, error) } diff --git a/statestore/mapRepository.go b/statestore/mapRepository.go index 124c60e..5f9ff7e 100644 --- a/statestore/mapRepository.go +++ b/statestore/mapRepository.go @@ -3,12 +3,18 @@ package statestore import ( "encoding/json" "io/ioutil" + "time" "github.com/sirupsen/logrus" ) +type mapEntry struct { + ModifyIndex int + LastSeen time.Time +} + type mapRepository struct { - db map[string]int + db map[string]mapEntry filename string } @@ -21,7 +27,7 @@ func NewMapRepository(filename string) Repository { return repo } - db := make(map[string]int) + db := make(map[string]mapEntry) repo.db = db err = repo.saveDatabase() @@ -34,16 +40,53 @@ func NewMapRepository(filename string) Repository { func (r *mapRepository) GetModifyIndex(hostname string) (int, error) { if value, ok := r.db[hostname]; ok { - return value, nil + r.SetModifyIndex(hostname, value.ModifyIndex) + return value.ModifyIndex, nil } return -1, nil } +func (r *mapRepository) GetOutdatedHosts(duration time.Duration) ([]string, error) { + var output []string + for k, v := range r.db { + + if v.LastSeen.Add(duration).Before(time.Now()) { + output = append(output, k) + } + + } + return output, nil +} + func (r *mapRepository) SetModifyIndex(hostname string, index int) error { - r.db[hostname] = index + if value, ok := r.db[hostname]; ok { + value.LastSeen = time.Now() + value.ModifyIndex = index + r.db[hostname] = value + } else { + value := new(mapEntry) + value.LastSeen = time.Now() + value.ModifyIndex = index + r.db[hostname] = *value + } return r.saveDatabase() } +func (r *mapRepository) DeleteHost(hostname string) error { + + logrus.Debugf("mapRepository: DeleteHost %s", hostname) + + delete(r.db, hostname) + return r.saveDatabase() +} + +func (r *mapRepository) DeleteHosts(hostnames []string) error { + for _, host := range hostnames { + r.DeleteHost(host) + } + return nil +} + func (r *mapRepository) openDatabase() error { logrus.Infof("mapRepository: openDatabase %s", r.filename) content, err := ioutil.ReadFile(r.filename) diff --git a/statestore/service.go b/statestore/service.go index e72a9c8..6d7583d 100644 --- a/statestore/service.go +++ b/statestore/service.go @@ -1,6 +1,10 @@ package statestore -import "github.com/sirupsen/logrus" +import ( + "time" + + "github.com/sirupsen/logrus" +) type service struct { r Repository @@ -30,3 +34,23 @@ func (s *service) SaveState(hostname string, index int) error { return s.r.SetModifyIndex(hostname, index) } + +func (s *service) GetStalledHosts(timeTreshold int) ([]string, error) { + return s.r.GetOutdatedHosts(time.Duration(timeTreshold) * time.Second) + +} + +func (s *service) PurgeStalledHosts(timeTreshold int) error { + logrus.Debugf("statestore: PurgeStalledHosts: %d", timeTreshold) + hosts, err := s.r.GetOutdatedHosts(time.Duration(timeTreshold) * time.Second) + if err != nil { + return err + } + + logrus.Debugf("statestore: PurgeStalledHosts: %+v", hosts) + + s.r.DeleteHosts(hosts) + + return nil + +}