sshfp-generator: New features and fixes
* Support for deleted hosts (cleanup records after configured time) * Small fixes Signed-off-by: Artur Marud <artur@status.im>
This commit is contained in:
parent
be748b44d6
commit
bf6021c8bb
|
@ -16,7 +16,8 @@ go build -mod vendor
|
||||||
Supported env variables:
|
Supported env variables:
|
||||||
`DOMAIN_NAME` - Domain name which will be working on
|
`DOMAIN_NAME` - Domain name which will be working on
|
||||||
`CF_TOKEN` - CloudFlare Token with write access to above domain
|
`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`)
|
It's possible to create json formatted config file (example in `testcfg`)
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ type Repository interface {
|
||||||
type Service interface {
|
type Service interface {
|
||||||
FindHostByName(hostname string) (bool, error)
|
FindHostByName(hostname string) (bool, error)
|
||||||
GetSSHFPRecordsForHost(hostname string) ([]*sshfp.SSHFPRecord, error)
|
GetSSHFPRecordsForHost(hostname string) ([]*sshfp.SSHFPRecord, error)
|
||||||
|
DeleteSSHFPRecordsForHost(hostname string) error
|
||||||
CreateSSHFPRecord(hostname string, record sshfp.SSHFPRecord) (int, error)
|
CreateSSHFPRecord(hostname string, record sshfp.SSHFPRecord) (int, error)
|
||||||
DeleteSSHFPRecord(hostname string, record sshfp.SSHFPRecord) error
|
DeleteSSHFPRecord(hostname string, record sshfp.SSHFPRecord) error
|
||||||
UpdateSSHFPRecord(hostname string, record sshfp.SSHFPRecord) error
|
UpdateSSHFPRecord(hostname string, record sshfp.SSHFPRecord) error
|
||||||
|
|
|
@ -63,6 +63,23 @@ func (s *service) GetSSHFPRecordsForHost(hostname string) ([]*sshfp.SSHFPRecord,
|
||||||
return output, nil
|
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) {
|
func (s *service) CreateSSHFPRecord(hostname string, record sshfp.SSHFPRecord) (int, error) {
|
||||||
logrus.Infof("cloudflare: CreateSSHFPRecord: %+v", record)
|
logrus.Infof("cloudflare: CreateSSHFPRecord: %+v", record)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ConsulToken string `json:"consulKey"`
|
//ConsulToken string `json:"consulKey"`
|
||||||
CloudflareToken string `json:"cloudflareKey"`
|
CloudflareToken string `json:"cloudflareKey"`
|
||||||
DomainName string `json:"domain"`
|
DomainName string `json:"domain"`
|
||||||
|
HostTimeout int `json:"hostLivenessTimeout"`
|
||||||
|
LogLevel string `json:"logLevel"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package config
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
@ -22,17 +23,27 @@ func (s *service) LoadConfig(fileName string) (*Config, error) {
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, errors.New("cannot find env variable CF_TOKEN")
|
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")
|
domainaName, exists := os.LookupEnv("DOMAIN_NAME")
|
||||||
if exists {
|
if !exists {
|
||||||
return nil, errors.New("cannot find env variable DOMAIN_NAME")
|
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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
22
main.go
22
main.go
|
@ -6,17 +6,24 @@ import (
|
||||||
"infra-sshfp-cf/consul"
|
"infra-sshfp-cf/consul"
|
||||||
"infra-sshfp-cf/sshfp"
|
"infra-sshfp-cf/sshfp"
|
||||||
"infra-sshfp-cf/statestore"
|
"infra-sshfp-cf/statestore"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
//Debug loglevel
|
//Debug loglevel
|
||||||
logrus.SetLevel(logrus.InfoLevel)
|
logrus.SetLevel(logrus.DebugLevel)
|
||||||
|
|
||||||
//Create configuration components
|
//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())
|
cfgService := config.NewService(config.NewFileRepository())
|
||||||
config, err := cfgService.LoadConfig("testcfg")
|
config, err := cfgService.LoadConfig(configFilename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,18 @@
|
||||||
package statestore
|
package statestore
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
type Repository interface {
|
type Repository interface {
|
||||||
GetModifyIndex(hostname string) (int, error)
|
GetModifyIndex(hostname string) (int, error)
|
||||||
SetModifyIndex(hostname string, index 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 {
|
type Service interface {
|
||||||
CheckIfModified(hostname string, index int) (bool, error)
|
CheckIfModified(hostname string, index int) (bool, error)
|
||||||
SaveState(hostname string, index int) error
|
SaveState(hostname string, index int) error
|
||||||
|
PurgeStalledHosts(timeTreshold int) error
|
||||||
|
GetStalledHosts(timeTreshold int) ([]string, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,18 @@ package statestore
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type mapEntry struct {
|
||||||
|
ModifyIndex int
|
||||||
|
LastSeen time.Time
|
||||||
|
}
|
||||||
|
|
||||||
type mapRepository struct {
|
type mapRepository struct {
|
||||||
db map[string]int
|
db map[string]mapEntry
|
||||||
filename string
|
filename string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +27,7 @@ func NewMapRepository(filename string) Repository {
|
||||||
return repo
|
return repo
|
||||||
}
|
}
|
||||||
|
|
||||||
db := make(map[string]int)
|
db := make(map[string]mapEntry)
|
||||||
repo.db = db
|
repo.db = db
|
||||||
|
|
||||||
err = repo.saveDatabase()
|
err = repo.saveDatabase()
|
||||||
|
@ -34,16 +40,53 @@ func NewMapRepository(filename string) Repository {
|
||||||
|
|
||||||
func (r *mapRepository) GetModifyIndex(hostname string) (int, error) {
|
func (r *mapRepository) GetModifyIndex(hostname string) (int, error) {
|
||||||
if value, ok := r.db[hostname]; ok {
|
if value, ok := r.db[hostname]; ok {
|
||||||
return value, nil
|
r.SetModifyIndex(hostname, value.ModifyIndex)
|
||||||
|
return value.ModifyIndex, nil
|
||||||
}
|
}
|
||||||
return -1, 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 {
|
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()
|
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 {
|
func (r *mapRepository) openDatabase() error {
|
||||||
logrus.Infof("mapRepository: openDatabase %s", r.filename)
|
logrus.Infof("mapRepository: openDatabase %s", r.filename)
|
||||||
content, err := ioutil.ReadFile(r.filename)
|
content, err := ioutil.ReadFile(r.filename)
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package statestore
|
package statestore
|
||||||
|
|
||||||
import "github.com/sirupsen/logrus"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
type service struct {
|
type service struct {
|
||||||
r Repository
|
r Repository
|
||||||
|
@ -30,3 +34,23 @@ func (s *service) SaveState(hostname string, index int) error {
|
||||||
return s.r.SetModifyIndex(hostname, index)
|
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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue