add refresh
This adds the facilities to refresh resources automatically.
This commit is contained in:
parent
bc410d2d10
commit
89e7ac94f5
|
@ -88,9 +88,9 @@ func listCertificate(args []string, client *pkiadm.Client) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
out := tabwriter.NewWriter(os.Stdout, 2, 2, 1, ' ', tabwriter.AlignRight)
|
out := tabwriter.NewWriter(os.Stdout, 2, 2, 1, ' ', tabwriter.AlignRight)
|
||||||
fmt.Fprintf(out, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n", "id", "private", "csr", "ca", "serial", "created", "duration", "self-signed")
|
fmt.Fprintf(out, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n", "id", "private", "csr", "ca", "serial", "created", "duration", "self-signed")
|
||||||
for _, cert := range certs {
|
for _, cert := range certs {
|
||||||
fmt.Fprintf(out, "%s\t%s\t%s\t%s\t%s\t%s\t%t\t\n", cert.ID, cert.PrivateKey.ID, cert.CSR.ID, cert.CA.ID, cert.Serial.ID, cert.Created, cert.Duration, cert.IsCA)
|
fmt.Fprintf(out, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%t\t\n", cert.ID, cert.PrivateKey.ID, cert.CSR.ID, cert.CA.ID, cert.Serial.ID, cert.Created, cert.Duration, cert.IsCA)
|
||||||
}
|
}
|
||||||
out.Flush()
|
out.Flush()
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/gibheer/pkiadm"
|
"github.com/gibheer/pkiadm"
|
||||||
|
@ -185,6 +186,7 @@ func list(args []string, c *pkiadm.Client) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
sort.Sort(resources)
|
||||||
out := tabwriter.NewWriter(os.Stdout, 0, 4, 1, ' ', 0)
|
out := tabwriter.NewWriter(os.Stdout, 0, 4, 1, ' ', 0)
|
||||||
fmt.Fprintf(out, "%s\t%s\t\n", "type", "id")
|
fmt.Fprintf(out, "%s\t%s\t\n", "type", "id")
|
||||||
for _, res := range resources {
|
for _, res := range resources {
|
||||||
|
|
|
@ -22,6 +22,7 @@ type (
|
||||||
ID string
|
ID string
|
||||||
Type pkiadm.CAType
|
Type pkiadm.CAType
|
||||||
Certificate pkiadm.ResourceName
|
Certificate pkiadm.ResourceName
|
||||||
|
Interval Interval
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -87,12 +88,18 @@ func (ca *CA) Name() pkiadm.ResourceName {
|
||||||
return pkiadm.ResourceName{ca.ID, pkiadm.RTCA}
|
return pkiadm.ResourceName{ca.ID, pkiadm.RTCA}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddDependency registers a depending resource to be retuened by Dependencies()
|
// Refresh must trigger a rebuild of the resource. In this case, this is a NOOP.
|
||||||
// Refresh must trigger a rebuild of the resource.
|
|
||||||
func (ca *CA) Refresh(*Storage) error {
|
func (ca *CA) Refresh(*Storage) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RefreshInterval returns the dates and interval settings which are used to
|
||||||
|
// decide when to trigger a refresh for the resource.
|
||||||
|
// For CAs, this is a NOOP, as the underlying cert needs the refresh.
|
||||||
|
func (ca *CA) RefreshInterval() Interval {
|
||||||
|
return NoInterval
|
||||||
|
}
|
||||||
|
|
||||||
// Return the PEM output of the contained resource.
|
// Return the PEM output of the contained resource.
|
||||||
func (ca *CA) Pem() ([]byte, error) { return []byte{}, nil }
|
func (ca *CA) Pem() ([]byte, error) { return []byte{}, nil }
|
||||||
func (ca *CA) Checksum() []byte { return []byte{} }
|
func (ca *CA) Checksum() []byte { return []byte{} }
|
||||||
|
|
|
@ -18,6 +18,8 @@ type (
|
||||||
ID string
|
ID string
|
||||||
|
|
||||||
IsCA bool
|
IsCA bool
|
||||||
|
Interval Interval
|
||||||
|
// TODO remove obsolete field - got replaced with interval
|
||||||
Duration time.Duration
|
Duration time.Duration
|
||||||
Created time.Time
|
Created time.Time
|
||||||
|
|
||||||
|
@ -31,6 +33,11 @@ type (
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewCertificate(id string, privateKey, serial, csr, ca pkiadm.ResourceName, selfSign bool, duration time.Duration) (*Certificate, error) {
|
func NewCertificate(id string, privateKey, serial, csr, ca pkiadm.ResourceName, selfSign bool, duration time.Duration) (*Certificate, error) {
|
||||||
|
|
||||||
|
if id == "" {
|
||||||
|
return nil, ENoIDGiven
|
||||||
|
}
|
||||||
|
|
||||||
return &Certificate{
|
return &Certificate{
|
||||||
ID: id,
|
ID: id,
|
||||||
PrivateKey: privateKey,
|
PrivateKey: privateKey,
|
||||||
|
@ -39,6 +46,10 @@ func NewCertificate(id string, privateKey, serial, csr, ca pkiadm.ResourceName,
|
||||||
CA: ca,
|
CA: ca,
|
||||||
IsCA: selfSign,
|
IsCA: selfSign,
|
||||||
Duration: duration,
|
Duration: duration,
|
||||||
|
Interval: Interval{
|
||||||
|
Created: time.Now(),
|
||||||
|
RefreshAfter: duration,
|
||||||
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,10 +97,16 @@ func (c *Certificate) Refresh(lookup *Storage) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.Data = pem.EncodeToMemory(&block)
|
c.Data = pem.EncodeToMemory(&block)
|
||||||
|
// TODO remove obsolete field
|
||||||
c.Created = time.Now()
|
c.Created = time.Now()
|
||||||
|
c.Interval.LastRefresh = time.Now()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Certificate) RefreshInterval() Interval {
|
||||||
|
return c.Interval
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Certificate) GetCertificate() (*pki.Certificate, error) {
|
func (c *Certificate) GetCertificate() (*pki.Certificate, error) {
|
||||||
// TODO fix this, we must check if there is anything else
|
// TODO fix this, we must check if there is anything else
|
||||||
block, _ := pem.Decode(c.Data)
|
block, _ := pem.Decode(c.Data)
|
||||||
|
@ -156,6 +173,7 @@ func (s *Server) SetCertificate(changeset pkiadm.CertificateChange, res *pkiadm.
|
||||||
switch field {
|
switch field {
|
||||||
case "duration":
|
case "duration":
|
||||||
cert.Duration = change.Duration
|
cert.Duration = change.Duration
|
||||||
|
cert.Interval.RefreshAfter = change.Duration
|
||||||
case "private":
|
case "private":
|
||||||
cert.PrivateKey = change.PrivateKey
|
cert.PrivateKey = change.PrivateKey
|
||||||
case "csr":
|
case "csr":
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gibheer/pki"
|
"github.com/gibheer/pki"
|
||||||
"github.com/gibheer/pkiadm"
|
"github.com/gibheer/pkiadm"
|
||||||
|
@ -14,6 +15,9 @@ type (
|
||||||
// ID is the unique identifier of the CSR.
|
// ID is the unique identifier of the CSR.
|
||||||
ID string
|
ID string
|
||||||
|
|
||||||
|
// Interval represents the refresh timing information.
|
||||||
|
Interval Interval
|
||||||
|
|
||||||
// The following options are used to generate the content of the CSR.
|
// The following options are used to generate the content of the CSR.
|
||||||
DNSNames []string
|
DNSNames []string
|
||||||
EmailAddresses []string
|
EmailAddresses []string
|
||||||
|
@ -30,7 +34,8 @@ type (
|
||||||
|
|
||||||
// NewCSR creates a new CSR.
|
// NewCSR creates a new CSR.
|
||||||
func NewCSR(id string, pk, subject pkiadm.ResourceName, dnsNames []string,
|
func NewCSR(id string, pk, subject pkiadm.ResourceName, dnsNames []string,
|
||||||
emailAddresses []string, iPAddresses []net.IP) (*CSR, error) {
|
emailAddresses []string, iPAddresses []net.IP, refreshAfter time.Duration,
|
||||||
|
invalidAfter time.Duration) (*CSR, error) {
|
||||||
return &CSR{
|
return &CSR{
|
||||||
ID: id,
|
ID: id,
|
||||||
Subject: subject,
|
Subject: subject,
|
||||||
|
@ -38,6 +43,11 @@ func NewCSR(id string, pk, subject pkiadm.ResourceName, dnsNames []string,
|
||||||
EmailAddresses: emailAddresses,
|
EmailAddresses: emailAddresses,
|
||||||
IPAddresses: iPAddresses,
|
IPAddresses: iPAddresses,
|
||||||
PrivateKey: pk,
|
PrivateKey: pk,
|
||||||
|
Interval: Interval{
|
||||||
|
Created: time.Now(),
|
||||||
|
RefreshAfter: refreshAfter,
|
||||||
|
InvalidAfter: invalidAfter,
|
||||||
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,9 +88,14 @@ func (c *CSR) Refresh(lookup *Storage) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.Data = pem.EncodeToMemory(&block)
|
c.Data = pem.EncodeToMemory(&block)
|
||||||
|
c.Interval.LastRefresh = time.Now()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CSR) RefreshInterval() Interval {
|
||||||
|
return c.Interval
|
||||||
|
}
|
||||||
|
|
||||||
// Return the PEM output of the contained resource.
|
// Return the PEM output of the contained resource.
|
||||||
func (c *CSR) Pem() ([]byte, error) { return c.Data, nil }
|
func (c *CSR) Pem() ([]byte, error) { return c.Data, nil }
|
||||||
func (c *CSR) Checksum() []byte { return Hash(c.Data) }
|
func (c *CSR) Checksum() []byte { return Hash(c.Data) }
|
||||||
|
@ -111,6 +126,7 @@ func (s *Server) CreateCSR(inCSR pkiadm.CSR, res *pkiadm.Result) error {
|
||||||
inCSR.DNSNames,
|
inCSR.DNSNames,
|
||||||
inCSR.EmailAddresses,
|
inCSR.EmailAddresses,
|
||||||
inCSR.IPAddresses,
|
inCSR.IPAddresses,
|
||||||
|
0, 0,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.SetError(err, "Could not create new private key '%s'", inCSR.ID)
|
res.SetError(err, "Could not create new private key '%s'", inCSR.ID)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gibheer/pkiadm"
|
"github.com/gibheer/pkiadm"
|
||||||
)
|
)
|
||||||
|
@ -23,10 +24,13 @@ type (
|
||||||
|
|
||||||
Path string
|
Path string
|
||||||
Dependencies []pkiadm.ResourceName
|
Dependencies []pkiadm.ResourceName
|
||||||
|
|
||||||
|
Interval Interval
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewLocation(id, path, preCom, postCom string, res []pkiadm.ResourceName) (*Location, error) {
|
func NewLocation(id, path, preCom, postCom string, res []pkiadm.ResourceName,
|
||||||
|
interval Interval) (*Location, error) {
|
||||||
if id == "" {
|
if id == "" {
|
||||||
return nil, ENoIDGiven
|
return nil, ENoIDGiven
|
||||||
}
|
}
|
||||||
|
@ -37,6 +41,7 @@ func NewLocation(id, path, preCom, postCom string, res []pkiadm.ResourceName) (*
|
||||||
ID: id,
|
ID: id,
|
||||||
Path: path,
|
Path: path,
|
||||||
Dependencies: res,
|
Dependencies: res,
|
||||||
|
Interval: interval,
|
||||||
}
|
}
|
||||||
return l, nil
|
return l, nil
|
||||||
}
|
}
|
||||||
|
@ -78,9 +83,14 @@ func (l *Location) Refresh(lookup *Storage) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
l.Interval.LastRefresh = time.Now()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Location) RefreshInterval() Interval {
|
||||||
|
return l.Interval
|
||||||
|
}
|
||||||
|
|
||||||
func (l *Location) DependsOn() []pkiadm.ResourceName { return l.Dependencies }
|
func (l *Location) DependsOn() []pkiadm.ResourceName { return l.Dependencies }
|
||||||
|
|
||||||
// Pem is not used by location, as it does not contain any data.
|
// Pem is not used by location, as it does not contain any data.
|
||||||
|
@ -97,7 +107,7 @@ func (s *Server) CreateLocation(inLoc pkiadm.Location, res *pkiadm.Result) error
|
||||||
for _, dep := range inLoc.Dependencies {
|
for _, dep := range inLoc.Dependencies {
|
||||||
deps = append(deps, pkiadm.ResourceName{ID: dep.ID, Type: dep.Type})
|
deps = append(deps, pkiadm.ResourceName{ID: dep.ID, Type: dep.Type})
|
||||||
}
|
}
|
||||||
loc, err := NewLocation(inLoc.ID, inLoc.Path, inLoc.PreCommand, inLoc.PostCommand, deps)
|
loc, err := NewLocation(inLoc.ID, inLoc.Path, inLoc.PreCommand, inLoc.PostCommand, deps, NoInterval)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.SetError(err, "Could not create location '%s'", inLoc.ID)
|
res.SetError(err, "Could not create location '%s'", inLoc.ID)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"net/rpc"
|
"net/rpc"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gibheer/pkiadm"
|
"github.com/gibheer/pkiadm"
|
||||||
)
|
)
|
||||||
|
@ -19,20 +20,42 @@ const (
|
||||||
EAlreadyExist = Error("resource already exists")
|
EAlreadyExist = Error("resource already exists")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
NoInterval = Interval{}
|
||||||
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
Resource interface {
|
Resource interface {
|
||||||
// Return the unique ResourceName
|
// Return the unique ResourceName
|
||||||
Name() pkiadm.ResourceName
|
Name() pkiadm.ResourceName
|
||||||
// AddDependency registers a depending resource to be retuened by Dependencies()
|
|
||||||
// Refresh must trigger a rebuild of the resource.
|
// Refresh must trigger a rebuild of the resource.
|
||||||
Refresh(*Storage) error
|
Refresh(*Storage) error
|
||||||
|
// RefreshInterval returns the dates and interval settings which are used to
|
||||||
|
// decide when to trigger a refresh for the resource.
|
||||||
|
RefreshInterval() Interval
|
||||||
// Return the PEM output of the contained resource.
|
// Return the PEM output of the contained resource.
|
||||||
Pem() ([]byte, error)
|
Pem() ([]byte, error)
|
||||||
|
// Return the checksum of the PEM content.
|
||||||
Checksum() []byte
|
Checksum() []byte
|
||||||
// DependsOn must return the resource names it is depending on.
|
// DependsOn must return the resource names it is depending on.
|
||||||
DependsOn() []pkiadm.ResourceName
|
DependsOn() []pkiadm.ResourceName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Interval struct {
|
||||||
|
// Created states the time, the resource was created.
|
||||||
|
Created time.Time
|
||||||
|
// LastRefresh is the time, when the resource was last refreshed.
|
||||||
|
LastRefresh time.Time
|
||||||
|
// RefreshAfter is the duration after which the refresh of the resource
|
||||||
|
// is triggered.
|
||||||
|
RefreshAfter time.Duration
|
||||||
|
// InvalidAfter is the duration after which this resource becomes invalid.
|
||||||
|
// The decision when a resource becomes invalid is based on the created time
|
||||||
|
// and the duration. When the refresh duration is less than the invalid
|
||||||
|
// duration, then the resource will never be invalid.
|
||||||
|
InvalidAfter time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
Error string
|
Error string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gibheer/pki"
|
"github.com/gibheer/pki"
|
||||||
"github.com/gibheer/pkiadm"
|
"github.com/gibheer/pkiadm"
|
||||||
|
@ -17,14 +18,15 @@ const (
|
||||||
|
|
||||||
type (
|
type (
|
||||||
PrivateKey struct {
|
PrivateKey struct {
|
||||||
ID string
|
ID string
|
||||||
PKType pkiadm.PrivateKeyType
|
PKType pkiadm.PrivateKeyType
|
||||||
Bits uint
|
Bits uint
|
||||||
Key []byte
|
Key []byte
|
||||||
|
Interval Interval
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewPrivateKey(id string, pkType pkiadm.PrivateKeyType, bits uint) (*PrivateKey, error) {
|
func NewPrivateKey(id string, pkType pkiadm.PrivateKeyType, bits uint, interval Interval) (*PrivateKey, error) {
|
||||||
if id == "" {
|
if id == "" {
|
||||||
return nil, ENoIDGiven
|
return nil, ENoIDGiven
|
||||||
}
|
}
|
||||||
|
@ -32,9 +34,10 @@ func NewPrivateKey(id string, pkType pkiadm.PrivateKeyType, bits uint) (*Private
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
pk := PrivateKey{
|
pk := PrivateKey{
|
||||||
ID: id,
|
ID: id,
|
||||||
PKType: pkType,
|
PKType: pkType,
|
||||||
Bits: bits,
|
Bits: bits,
|
||||||
|
Interval: interval,
|
||||||
}
|
}
|
||||||
return &pk, nil
|
return &pk, nil
|
||||||
}
|
}
|
||||||
|
@ -88,9 +91,16 @@ func (p *PrivateKey) Refresh(_ *Storage) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.Key = pem.EncodeToMemory(&block)
|
p.Key = pem.EncodeToMemory(&block)
|
||||||
|
p.Interval.LastRefresh = time.Now()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RefreshInterval returns the dates and interval settings which are used to
|
||||||
|
// decide when to trigger a refresh for the resource.
|
||||||
|
func (p *PrivateKey) RefreshInterval() Interval {
|
||||||
|
return p.Interval
|
||||||
|
}
|
||||||
|
|
||||||
func (p *PrivateKey) GetKey() (pki.PrivateKey, error) {
|
func (p *PrivateKey) GetKey() (pki.PrivateKey, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
|
@ -139,7 +149,7 @@ func (s *Server) CreatePrivateKey(inPk pkiadm.PrivateKey, res *pkiadm.Result) er
|
||||||
s.lock()
|
s.lock()
|
||||||
defer s.unlock()
|
defer s.unlock()
|
||||||
|
|
||||||
pk, err := NewPrivateKey(inPk.ID, inPk.Type, inPk.Bits)
|
pk, err := NewPrivateKey(inPk.ID, inPk.Type, inPk.Bits, NoInterval)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.SetError(err, "Could not create new private key '%s'", inPk.ID)
|
res.SetError(err, "Could not create new private key '%s'", inPk.ID)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gibheer/pkiadm"
|
"github.com/gibheer/pkiadm"
|
||||||
)
|
)
|
||||||
|
@ -14,13 +15,21 @@ type (
|
||||||
PrivateKey pkiadm.ResourceName
|
PrivateKey pkiadm.ResourceName
|
||||||
Type pkiadm.PrivateKeyType // mark the type of the public key
|
Type pkiadm.PrivateKeyType // mark the type of the public key
|
||||||
Key []byte
|
Key []byte
|
||||||
|
|
||||||
|
Interval Interval
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewPublicKey(id string, pk pkiadm.ResourceName) (*PublicKey, error) {
|
func NewPublicKey(id string, pk pkiadm.ResourceName, refreshAfter time.Duration,
|
||||||
|
invalidAfter time.Duration) (*PublicKey, error) {
|
||||||
pub := PublicKey{
|
pub := PublicKey{
|
||||||
ID: id,
|
ID: id,
|
||||||
PrivateKey: pk,
|
PrivateKey: pk,
|
||||||
|
Interval: Interval{
|
||||||
|
Created: time.Now(),
|
||||||
|
RefreshAfter: refreshAfter,
|
||||||
|
InvalidAfter: invalidAfter,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return &pub, nil
|
return &pub, nil
|
||||||
}
|
}
|
||||||
|
@ -45,9 +54,14 @@ func (p *PublicKey) Refresh(lookup *Storage) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.Key = pem.EncodeToMemory(&block)
|
p.Key = pem.EncodeToMemory(&block)
|
||||||
|
p.Interval.LastRefresh = time.Now()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PublicKey) RefreshInterval() Interval {
|
||||||
|
return p.Interval
|
||||||
|
}
|
||||||
|
|
||||||
func (p *PublicKey) DependsOn() []pkiadm.ResourceName {
|
func (p *PublicKey) DependsOn() []pkiadm.ResourceName {
|
||||||
return []pkiadm.ResourceName{p.PrivateKey}
|
return []pkiadm.ResourceName{p.PrivateKey}
|
||||||
}
|
}
|
||||||
|
@ -64,7 +78,7 @@ func (s *Server) CreatePublicKey(inPub pkiadm.PublicKey, res *pkiadm.Result) err
|
||||||
s.lock()
|
s.lock()
|
||||||
defer s.unlock()
|
defer s.unlock()
|
||||||
|
|
||||||
pub, err := NewPublicKey(inPub.ID, inPub.PrivateKey)
|
pub, err := NewPublicKey(inPub.ID, inPub.PrivateKey, 0, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.SetError(err, "Could not create public key '%s'", inPub.ID)
|
res.SetError(err, "Could not create public key '%s'", inPub.ID)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -33,7 +33,6 @@ func NewSerial(id string, min, max int64) (*Serial, error) {
|
||||||
// Return the unique ResourceName
|
// Return the unique ResourceName
|
||||||
func (s *Serial) Name() pkiadm.ResourceName { return pkiadm.ResourceName{s.ID, pkiadm.RTSerial} }
|
func (s *Serial) Name() pkiadm.ResourceName { return pkiadm.ResourceName{s.ID, pkiadm.RTSerial} }
|
||||||
|
|
||||||
// AddDependency registers a depending resource to be retuened by Dependencies()
|
|
||||||
// Refresh must trigger a rebuild of the resource.
|
// Refresh must trigger a rebuild of the resource.
|
||||||
func (s *Serial) Refresh(*Storage) error {
|
func (s *Serial) Refresh(*Storage) error {
|
||||||
// This is a NOOP, because there is nothing to refresh. Depending resources
|
// This is a NOOP, because there is nothing to refresh. Depending resources
|
||||||
|
@ -41,6 +40,11 @@ func (s *Serial) Refresh(*Storage) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RefreshInterval is a NOOP here, as serials can't be refreshed.
|
||||||
|
func (s *Serial) RefreshInterval() Interval {
|
||||||
|
return NoInterval
|
||||||
|
}
|
||||||
|
|
||||||
// Return the PEM output of the contained resource.
|
// Return the PEM output of the contained resource.
|
||||||
func (s *Serial) Pem() ([]byte, error) { return []byte{}, nil }
|
func (s *Serial) Pem() ([]byte, error) { return []byte{}, nil }
|
||||||
func (s *Serial) Checksum() []byte { return []byte{} }
|
func (s *Serial) Checksum() []byte { return []byte{} }
|
||||||
|
|
|
@ -33,26 +33,10 @@ func (s *Server) store(res *pkiadm.Result) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) List(filter pkiadm.Filter, result *pkiadm.ResultResource) error {
|
func (s *Server) List(filter pkiadm.Filter, result *pkiadm.ResultResource) error {
|
||||||
for _, res := range s.storage.PrivateKeys {
|
resources := s.storage.List()
|
||||||
result.Resources = append(result.Resources, res.Name())
|
result.Resources = make([]pkiadm.ResourceName, len(resources))
|
||||||
}
|
for i, res := range resources {
|
||||||
for _, res := range s.storage.PublicKeys {
|
result.Resources[i] = res.Name()
|
||||||
result.Resources = append(result.Resources, res.Name())
|
|
||||||
}
|
|
||||||
for _, res := range s.storage.Locations {
|
|
||||||
result.Resources = append(result.Resources, res.Name())
|
|
||||||
}
|
|
||||||
for _, res := range s.storage.Certificates {
|
|
||||||
result.Resources = append(result.Resources, res.Name())
|
|
||||||
}
|
|
||||||
for _, res := range s.storage.CSRs {
|
|
||||||
result.Resources = append(result.Resources, res.Name())
|
|
||||||
}
|
|
||||||
for _, res := range s.storage.Serials {
|
|
||||||
result.Resources = append(result.Resources, res.Name())
|
|
||||||
}
|
|
||||||
for _, res := range s.storage.Subjects {
|
|
||||||
result.Resources = append(result.Resources, res.Name())
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gibheer/pkiadm"
|
"github.com/gibheer/pkiadm"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -28,6 +30,19 @@ type (
|
||||||
// dependencies maps from a resource name to all resources which depend
|
// dependencies maps from a resource name to all resources which depend
|
||||||
// on it.
|
// on it.
|
||||||
dependencies map[string]map[string]Resource
|
dependencies map[string]map[string]Resource
|
||||||
|
// refresh order contains all resources in the order they need to be
|
||||||
|
// refreshed next.
|
||||||
|
refreshOrder RefreshList
|
||||||
|
refreshTimer *time.Timer
|
||||||
|
}
|
||||||
|
|
||||||
|
// RefreshList is a list of resources
|
||||||
|
RefreshList []RefreshSet
|
||||||
|
// RefreshSet contains the vital information to decide, when to refresh
|
||||||
|
// the specified resource.
|
||||||
|
RefreshSet struct {
|
||||||
|
Name pkiadm.ResourceName
|
||||||
|
Interval Interval
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -68,6 +83,7 @@ func (s *Storage) load() error {
|
||||||
if err := s.refreshDependencies(); err != nil {
|
if err := s.refreshDependencies(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
s.scanForRefresh()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,6 +117,69 @@ func (s *Storage) refreshDependencies() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// scanForRefresh updates the list of resources that need an update.
|
||||||
|
func (s *Storage) scanForRefresh() {
|
||||||
|
if s.refreshTimer != nil {
|
||||||
|
s.refreshTimer.Stop()
|
||||||
|
}
|
||||||
|
refList := RefreshList{}
|
||||||
|
for _, res := range s.PrivateKeys {
|
||||||
|
refList.Add(res)
|
||||||
|
}
|
||||||
|
for _, res := range s.PublicKeys {
|
||||||
|
refList.Add(res)
|
||||||
|
}
|
||||||
|
for _, res := range s.CSRs {
|
||||||
|
refList.Add(res)
|
||||||
|
}
|
||||||
|
for _, res := range s.Certificates {
|
||||||
|
refList.Add(res)
|
||||||
|
}
|
||||||
|
for _, res := range s.Locations {
|
||||||
|
refList.Add(res)
|
||||||
|
}
|
||||||
|
sort.Sort(refList)
|
||||||
|
if len(refList) == 0 {
|
||||||
|
log.Println("nothing found to refresh, looking again in 24h")
|
||||||
|
s.refreshTimer = time.AfterFunc(24*time.Hour, s.scanForRefresh)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.refreshOrder = refList
|
||||||
|
duration := refList[0].Interval.LastRefresh.
|
||||||
|
Add(refList[0].Interval.RefreshAfter).
|
||||||
|
Sub(time.Now())
|
||||||
|
if duration <= 5*time.Second {
|
||||||
|
duration = 5 * time.Second
|
||||||
|
}
|
||||||
|
log.Printf("next refresh planned for '%s' in %s", refList[0].Name, duration)
|
||||||
|
s.refreshTimer = time.AfterFunc(
|
||||||
|
duration,
|
||||||
|
s.refresh,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Storage) refresh() {
|
||||||
|
if len(s.refreshOrder) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resName := s.refreshOrder[0].Name
|
||||||
|
res, err := s.Get(resName)
|
||||||
|
if err != nil {
|
||||||
|
// the resource doesn't exist anymore, so just rescan
|
||||||
|
log.Printf("resource to refresh has gone away: %s", resName)
|
||||||
|
goto rescan
|
||||||
|
}
|
||||||
|
if err := res.Refresh(s); err != nil {
|
||||||
|
log.Printf("error refreshing resource '%s': %s", res.Name(), err)
|
||||||
|
}
|
||||||
|
if err := s.store(); err != nil {
|
||||||
|
log.Printf("could not update resources: %s", err)
|
||||||
|
}
|
||||||
|
rescan:
|
||||||
|
log.Printf("rescanning for new entries")
|
||||||
|
s.scanForRefresh()
|
||||||
|
}
|
||||||
|
|
||||||
// addDependency adds a resource to the dependency graph.
|
// addDependency adds a resource to the dependency graph.
|
||||||
func (s *Storage) addDependency(r Resource) error {
|
func (s *Storage) addDependency(r Resource) error {
|
||||||
for _, rn := range r.DependsOn() {
|
for _, rn := range r.DependsOn() {
|
||||||
|
@ -138,6 +217,7 @@ func (s *Storage) AddSerial(se *Serial) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.Serials[se.Name().ID] = se
|
s.Serials[se.Name().ID] = se
|
||||||
|
s.scanForRefresh()
|
||||||
return s.addDependency(se)
|
return s.addDependency(se)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,6 +230,7 @@ func (s *Storage) AddSubject(se *Subject) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.Subjects[se.Name().ID] = se
|
s.Subjects[se.Name().ID] = se
|
||||||
|
s.scanForRefresh()
|
||||||
return s.addDependency(se)
|
return s.addDependency(se)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,6 +240,7 @@ func (s *Storage) AddPrivateKey(pk *PrivateKey) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.PrivateKeys[pk.Name().ID] = pk
|
s.PrivateKeys[pk.Name().ID] = pk
|
||||||
|
s.scanForRefresh()
|
||||||
return s.addDependency(pk)
|
return s.addDependency(pk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,6 +250,7 @@ func (s *Storage) AddPublicKey(pub *PublicKey) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.PublicKeys[pub.Name().ID] = pub
|
s.PublicKeys[pub.Name().ID] = pub
|
||||||
|
s.scanForRefresh()
|
||||||
return s.addDependency(pub)
|
return s.addDependency(pub)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,6 +260,7 @@ func (s *Storage) AddCertificate(cert *Certificate) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.Certificates[cert.Name().ID] = cert
|
s.Certificates[cert.Name().ID] = cert
|
||||||
|
s.scanForRefresh()
|
||||||
return s.addDependency(cert)
|
return s.addDependency(cert)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,6 +270,7 @@ func (s *Storage) AddCSR(csr *CSR) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.CSRs[csr.Name().ID] = csr
|
s.CSRs[csr.Name().ID] = csr
|
||||||
|
s.scanForRefresh()
|
||||||
return s.addDependency(csr)
|
return s.addDependency(csr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,6 +280,7 @@ func (s *Storage) AddLocation(l *Location) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.Locations[l.Name().ID] = l
|
s.Locations[l.Name().ID] = l
|
||||||
|
s.scanForRefresh()
|
||||||
return s.addDependency(l)
|
return s.addDependency(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,6 +289,7 @@ func (s *Storage) AddCA(ca *CA) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.CAs[ca.Name().ID] = ca
|
s.CAs[ca.Name().ID] = ca
|
||||||
|
s.scanForRefresh()
|
||||||
return s.addDependency(ca)
|
return s.addDependency(ca)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,6 +413,7 @@ func (s *Storage) Remove(r Resource) error {
|
||||||
delete(deps, r.Name().String())
|
delete(deps, r.Name().String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
s.scanForRefresh()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,5 +452,65 @@ func (s *Storage) Update(rn pkiadm.ResourceName) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
s.scanForRefresh()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// List returns all currently registered resources.
|
||||||
|
func (s *Storage) List() []Resource {
|
||||||
|
resources := []Resource{}
|
||||||
|
for _, res := range s.PrivateKeys {
|
||||||
|
resources = append(resources, res)
|
||||||
|
}
|
||||||
|
for _, res := range s.PublicKeys {
|
||||||
|
resources = append(resources, res)
|
||||||
|
}
|
||||||
|
for _, res := range s.Locations {
|
||||||
|
resources = append(resources, res)
|
||||||
|
}
|
||||||
|
for _, res := range s.Certificates {
|
||||||
|
resources = append(resources, res)
|
||||||
|
}
|
||||||
|
for _, res := range s.CSRs {
|
||||||
|
resources = append(resources, res)
|
||||||
|
}
|
||||||
|
for _, res := range s.Serials {
|
||||||
|
resources = append(resources, res)
|
||||||
|
}
|
||||||
|
for _, res := range s.Subjects {
|
||||||
|
resources = append(resources, res)
|
||||||
|
}
|
||||||
|
return resources
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a resource to the refreshList when it should be refreshed.
|
||||||
|
func (refList *RefreshList) Add(res Resource) {
|
||||||
|
refSet := RefreshSet{
|
||||||
|
Name: res.Name(),
|
||||||
|
Interval: res.RefreshInterval(),
|
||||||
|
}
|
||||||
|
if refSet.Interval.RefreshAfter <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newRefList := append(*refList, refSet)
|
||||||
|
*refList = newRefList
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len is the number of elements in the collection.
|
||||||
|
func (refList RefreshList) Len() int { return len(refList) }
|
||||||
|
|
||||||
|
// Less reports whether the element with
|
||||||
|
// index i should sort before the element with index j.
|
||||||
|
func (refList RefreshList) Less(i, j int) bool {
|
||||||
|
return 0 > refList[i].Interval.LastRefresh.Add(
|
||||||
|
refList[i].Interval.RefreshAfter,
|
||||||
|
).Sub(
|
||||||
|
refList[j].Interval.LastRefresh.Add(
|
||||||
|
refList[j].Interval.RefreshAfter),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap swaps the elements with indexes i and j.
|
||||||
|
func (refList RefreshList) Swap(i, j int) {
|
||||||
|
refList[i], refList[j] = refList[j], refList[i]
|
||||||
|
}
|
||||||
|
|
|
@ -3,14 +3,16 @@ package main
|
||||||
import (
|
import (
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gibheer/pkiadm"
|
"github.com/gibheer/pkiadm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
Subject struct {
|
Subject struct {
|
||||||
ID string
|
ID string
|
||||||
Data pkix.Name
|
Data pkix.Name
|
||||||
|
Created time.Time
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,11 +28,14 @@ func NewSubject(id string, name pkix.Name) (*Subject, error) {
|
||||||
// Return the unique ResourceName
|
// Return the unique ResourceName
|
||||||
func (sub *Subject) Name() pkiadm.ResourceName { return pkiadm.ResourceName{sub.ID, pkiadm.RTSubject} }
|
func (sub *Subject) Name() pkiadm.ResourceName { return pkiadm.ResourceName{sub.ID, pkiadm.RTSubject} }
|
||||||
|
|
||||||
// AddDependency registers a depending resource to be retuened by Dependencies()
|
|
||||||
// Refresh must trigger a rebuild of the resource.
|
// Refresh must trigger a rebuild of the resource.
|
||||||
// This is a NOOP as it does not have any dependencies.
|
// This is a NOOP as it does not have any dependencies.
|
||||||
func (sub *Subject) Refresh(_ *Storage) error { return nil }
|
func (sub *Subject) Refresh(_ *Storage) error { return nil }
|
||||||
|
|
||||||
|
// RefreshInterval returns the dates and interval settings which are used to
|
||||||
|
// decide when to trigger a refresh for the resource.
|
||||||
|
func (sub *Subject) RefreshInterval() Interval { return Interval{Created: sub.Created} }
|
||||||
|
|
||||||
// Return the PEM output of the contained resource.
|
// Return the PEM output of the contained resource.
|
||||||
func (sub *Subject) Pem() ([]byte, error) { return []byte{}, nil }
|
func (sub *Subject) Pem() ([]byte, error) { return []byte{}, nil }
|
||||||
func (sub *Subject) Checksum() []byte { return []byte{} }
|
func (sub *Subject) Checksum() []byte { return []byte{} }
|
||||||
|
|
17
transport.go
17
transport.go
|
@ -49,6 +49,21 @@ type ResourceType uint
|
||||||
|
|
||||||
func (r ResourceName) String() string { return r.Type.String() + "/" + r.ID }
|
func (r ResourceName) String() string { return r.Type.String() + "/" + r.ID }
|
||||||
|
|
||||||
|
type ResourceNameList []ResourceName
|
||||||
|
|
||||||
|
func (r ResourceNameList) Len() int {
|
||||||
|
return len(r)
|
||||||
|
}
|
||||||
|
func (r ResourceNameList) Less(i, j int) bool {
|
||||||
|
if r[i].Type != r[j].Type {
|
||||||
|
return r[i].Type < r[j].Type
|
||||||
|
}
|
||||||
|
return r[i].ID < r[j].ID
|
||||||
|
}
|
||||||
|
func (r ResourceNameList) Swap(i, j int) {
|
||||||
|
r[i], r[j] = r[j], r[i]
|
||||||
|
}
|
||||||
|
|
||||||
type Filter struct{}
|
type Filter struct{}
|
||||||
|
|
||||||
type ResultResource struct {
|
type ResultResource struct {
|
||||||
|
@ -56,7 +71,7 @@ type ResultResource struct {
|
||||||
Resources []ResourceName
|
Resources []ResourceName
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) List() ([]ResourceName, error) {
|
func (c *Client) List() (ResourceNameList, error) {
|
||||||
result := ResultResource{}
|
result := ResultResource{}
|
||||||
if err := c.query("List", Filter{}, &result); err != nil {
|
if err := c.query("List", Filter{}, &result); err != nil {
|
||||||
return []ResourceName{}, err
|
return []ResourceName{}, err
|
||||||
|
|
Loading…
Reference in New Issue