-
Notifications
You must be signed in to change notification settings - Fork 64
/
origin.go
147 lines (119 loc) · 3.59 KB
/
origin.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Package provisioners provides a mapping between CertificateRequest
// and the Cloudflare API, with credentials already bounded by an
// OriginIssuer.
package provisioners
import (
"context"
"fmt"
"math"
"sync"
certmanager "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
"github.com/cert-manager/cert-manager/pkg/util/pki"
"github.com/cloudflare/origin-ca-issuer/internal/cfapi"
v1 "github.com/cloudflare/origin-ca-issuer/pkgs/apis/v1"
"github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/types"
)
const (
// The default validity duration, if not provided.
DefaultDurationInternval = 7
)
var allowedValidty = []int{7, 30, 90, 365, 730, 1095, 5475}
// Collection stores cached Provisioners, stored by namespaced names of the
// issuer.
type Collection struct {
m sync.Map
}
// A CollectionItem allows for the namespaced name and provisioner to
// be stored together.
type CollectionItem struct {
NamespacedName types.NamespacedName
Provisioner *Provisioner
}
// CollectionWith returns a Collection storing the provided provisioners.
func CollectionWith(items []CollectionItem) *Collection {
c := &Collection{}
for _, i := range items {
c.Store(i.NamespacedName, i.Provisioner)
}
return c
}
// Provisioner allows for CertificateRequests to be signed using the stored
// Cloudflare API client.
type Provisioner struct {
client Signer
log logr.Logger
reqType v1.RequestType
}
// Signer implements the Origin CA signing API.
type Signer interface {
Sign(ctx context.Context, req *cfapi.SignRequest) (*cfapi.SignResponse, error)
}
// New returns a new provisioner.
func New(client Signer, reqType v1.RequestType, log logr.Logger) (*Provisioner, error) {
p := &Provisioner{
client: client,
log: log,
reqType: reqType,
}
return p, nil
}
// Store adds a provisioner to the collection.
func (c *Collection) Store(namespacedName types.NamespacedName, provisioner *Provisioner) {
c.m.Store(namespacedName, provisioner)
}
// Load returns the stored provisioner, or returns false if nothing is cached with
// the proved namespaced name.
func (c *Collection) Load(namespacedName types.NamespacedName) (*Provisioner, bool) {
v, ok := c.m.Load(namespacedName)
if !ok {
return nil, ok
}
p, ok := v.(*Provisioner)
return p, ok
}
// Sign uses the Cloduflare API to sign a CertificateRequest. The validity of the CertificateRequest is
// normalized to the closests validity allowed by the Cloudflare API, which make be significantly different
// than the validity provided.
func (p *Provisioner) Sign(ctx context.Context, cr *certmanager.CertificateRequest) (certPem []byte, err error) {
csr, err := pki.DecodeX509CertificateRequestBytes(cr.Spec.Request)
if err != nil {
return nil, fmt.Errorf("failed to decode CSR for signing: %s", err)
}
hostnames := csr.DNSNames
var duration int
if cr.Spec.Duration == nil {
duration = DefaultDurationInternval
} else {
duration = closest(int(cr.Spec.Duration.Duration.Hours()/24), allowedValidty)
}
var reqType string
switch p.reqType {
case v1.RequestTypeOriginECC:
reqType = "origin-ecc"
case v1.RequestTypeOriginRSA:
reqType = "origin-rsa"
}
resp, err := p.client.Sign(ctx, &cfapi.SignRequest{
Hostnames: hostnames,
Validity: duration,
Type: reqType,
CSR: string(cr.Spec.Request),
})
if err != nil {
return nil, fmt.Errorf("unable to sign request: %w", err)
}
return []byte(resp.Certificate), nil
}
func closest(of int, valid []int) int {
min := math.MaxFloat64
closest := of
for _, v := range valid {
diff := math.Abs(float64(v - of))
if diff < min {
min = diff
closest = v
}
}
return closest
}