Skip to content

Commit b0c22d3

Browse files
authored
Added auto-renewal of nebula certificates and GC on badgerDB (#22)
* Added auto-renewal of nebula certificates and GC on badgerDB
1 parent c89e42a commit b0c22d3

6 files changed

Lines changed: 160 additions & 6 deletions

File tree

examples/server.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,9 @@
2727
# authUrl: ""
2828
# tokenUrl: ""
2929
# userInfoUrl: ""
30+
31+
#tasks:
32+
# certRenew:
33+
# interval: 1h
34+
# dbGC:
35+
# interval: 5m

server/server.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type server struct {
2525
ipManager *store.IPManager
2626
unixGrpc *grpc.Server
2727
agentService *grpc.Server
28+
tasks *tasks
2829
}
2930

3031
func Main(config *nebula.Config, buildVersion string, logger *logrus.Logger) (*Control, error) {
@@ -33,7 +34,7 @@ func Main(config *nebula.Config, buildVersion string, logger *logrus.Logger) (*C
3334
FullTimestamp: true,
3435
}
3536

36-
server := server{l, config, buildVersion, false, nil, nil, nil, nil}
37+
server := server{l, config, buildVersion, false, nil, nil, nil, nil, nil}
3738

3839
return &Control{l, server.start, server.stop, make(chan interface{})}, nil
3940
}
@@ -91,12 +92,18 @@ func (s *server) start() error {
9192
if err != nil {
9293
return err
9394
}
95+
s.tasks = NewTasks(s.l, s.config, s.store)
96+
s.tasks.Start()
9497
}
9598

9699
return nil
97100
}
98101

99102
func (s *server) stop() {
103+
if s.tasks != nil {
104+
s.tasks.Stop()
105+
}
106+
100107
if err := s.stopAgentService(); err != nil {
101108
s.l.WithError(err).Error("Failed to stop agentService server")
102109
}

server/store/agent.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package store
22

33
import (
44
"fmt"
5+
"time"
56

67
"github.com/dgraph-io/badger/v3"
78
"github.com/golang/protobuf/proto"
@@ -50,6 +51,23 @@ func (s *Store) ListAgentByNetwork(networkName string) ([]*Agent, error) {
5051
return s.listAgentByNetwork(txn, networkName)
5152
}
5253

54+
func (s Store) RenewCertForAgents() error {
55+
txn := s.db.NewTransaction(true)
56+
defer txn.Discard()
57+
58+
err := s.renewCertForAgents(txn)
59+
if err != nil {
60+
return fmt.Errorf("failed to new certificates for agents")
61+
}
62+
63+
err = txn.Commit()
64+
if err != nil {
65+
return fmt.Errorf("failed to add enrollment token: %s", err)
66+
}
67+
68+
return nil
69+
}
70+
5371
func (s *Store) isAgentEnrolled(txn *badger.Txn, fingerprint []byte) bool {
5472
return exists(txn, prefix_agent, fingerprint)
5573
}
@@ -136,3 +154,50 @@ func (s *Store) deleteAgent(txn *badger.Txn, fingerprint []byte) error {
136154
}
137155
return nil
138156
}
157+
158+
func (s Store) renewCertForAgents(txn *badger.Txn) error {
159+
renewThreshold := 7 * 24 * time.Hour
160+
161+
opts := badger.DefaultIteratorOptions
162+
opts.PrefetchSize = 10
163+
opts.Prefix = prefix_agent
164+
it := txn.NewIterator(opts)
165+
defer it.Close()
166+
167+
for it.Seek(prefix_agent); it.ValidForPrefix(prefix_agent); it.Next() {
168+
item := it.Item()
169+
err := item.Value(func(v []byte) error {
170+
agent := &Agent{}
171+
if err := proto.Unmarshal(v, agent); err != nil {
172+
s.l.WithError(err).Error("Failed to parse agent")
173+
return nil
174+
}
175+
176+
untilExpires := time.Until(agent.ExpiresAt.AsTime())
177+
178+
if untilExpires.Hours() < renewThreshold.Hours() {
179+
s.l.Debugf("Renewing certificate for agent: %s %x", agent.Name, agent.Fingerprint)
180+
ip, err := assignedIPToIPNet(agent.AssignedIP)
181+
if err != nil {
182+
return fmt.Errorf("failed to parse ip of agent: %s", err)
183+
}
184+
185+
agent, err = s.signCSR(txn, agent, ip)
186+
if err != nil {
187+
return fmt.Errorf("failed to sign agent csr: %s", err)
188+
}
189+
190+
agent, err = s.updateAgent(txn, agent)
191+
if err != nil {
192+
return fmt.Errorf("failed to update agent as part of renewing agent cerfiticate: %s", err)
193+
}
194+
}
195+
196+
return nil
197+
})
198+
if err != nil {
199+
return err
200+
}
201+
}
202+
return nil
203+
}

server/store/enrollment.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -343,14 +343,10 @@ func (s *Store) approveEnrollmentRequest(txn *badger.Txn, ipManager *IPManager,
343343
}
344344

345345
if enrolled {
346-
i, n, err := net.ParseCIDR(agent.AssignedIP)
346+
ip, err = assignedIPToIPNet(agent.AssignedIP)
347347
if err != nil {
348348
return nil, fmt.Errorf("failed to parse ip of existing agent: %s", err)
349349
}
350-
ip = &net.IPNet{
351-
IP: i,
352-
Mask: n.Mask,
353-
}
354350
}
355351

356352
if ip == nil {

server/store/store.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"encoding/hex"
77
"fmt"
88
"io/ioutil"
9+
"net"
910
"os"
1011
"path"
1112
"path/filepath"
@@ -135,6 +136,14 @@ func (s *Store) Unseal(keyPart string, removeExistingParts bool) error {
135136
return nil
136137
}
137138

139+
func (s *Store) GC() {
140+
again:
141+
err := s.db.RunValueLogGC(0.7)
142+
if err == nil {
143+
goto again
144+
}
145+
}
146+
138147
func NewStore(l *logrus.Logger, dataDir string, unsealed chan interface{}, encryptionEnabled bool) (*Store, error) {
139148
dbPath := filepath.Join(dataDir, "db")
140149
stat, err := os.Stat(dbPath)
@@ -208,3 +217,14 @@ func containsByteSlice(array [][]byte, value []byte) bool {
208217
}
209218
return false
210219
}
220+
221+
func assignedIPToIPNet(assignedIP string) (*net.IPNet, error) {
222+
i, n, err := net.ParseCIDR(assignedIP)
223+
if err != nil {
224+
return nil, err
225+
}
226+
return &net.IPNet{
227+
IP: i,
228+
Mask: n.Mask,
229+
}, nil
230+
}

server/tasks.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package server
2+
3+
import (
4+
"time"
5+
6+
"github.com/sirupsen/logrus"
7+
"github.com/slackhq/nebula"
8+
"github.com/slyngdk/nebula-provisioner/server/store"
9+
)
10+
11+
type tasks struct {
12+
config *nebula.Config
13+
store *store.Store
14+
15+
l *logrus.Logger
16+
quit chan interface{}
17+
}
18+
19+
func NewTasks(l *logrus.Logger, config *nebula.Config, store *store.Store) *tasks {
20+
return &tasks{l: l, config: config, store: store, quit: make(chan interface{})}
21+
}
22+
23+
func (t *tasks) Start() {
24+
t.l.Infoln("Starting task scheduler")
25+
26+
renewCertTicker := time.NewTicker(t.config.GetDuration("tasks.certRenew.interval", 1*time.Hour))
27+
dbGCTicker := time.NewTicker(t.config.GetDuration("tasks.dbGC.interval", 5*time.Minute))
28+
29+
go func() {
30+
for {
31+
select {
32+
case <-renewCertTicker.C:
33+
t.renewCerts()
34+
case <-dbGCTicker.C:
35+
t.dbGC()
36+
case <-t.quit:
37+
renewCertTicker.Stop()
38+
return
39+
}
40+
}
41+
}()
42+
}
43+
44+
func (t *tasks) Stop() {
45+
t.l.Infoln("Stopping task scheduler")
46+
t.quit <- struct{}{}
47+
}
48+
49+
func (t *tasks) renewCerts() {
50+
t.l.Infoln("Task: renew agent certificates")
51+
err := t.store.RenewCertForAgents()
52+
if err != nil {
53+
t.l.WithError(err).Errorln("error when renewing certificates for agents")
54+
}
55+
}
56+
57+
func (t *tasks) dbGC() {
58+
t.l.Debugln("Task: db garbage collection")
59+
t.store.GC()
60+
}

0 commit comments

Comments
 (0)