-
Notifications
You must be signed in to change notification settings - Fork 0
/
oci.go
148 lines (134 loc) · 5.19 KB
/
oci.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
148
package provisioner
import (
"context"
"fmt"
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
"github.com/cert-manager/cert-manager/pkg/util/pki"
"github.com/go-logr/logr"
"github.com/oracle/oci-go-sdk/v65/certificates"
"github.com/oracle/oci-go-sdk/v65/certificatesmanagement"
"github.com/oracle/oci-go-sdk/v65/common"
ocicav1alpha1 "github.com/william20111/oci-privateca-issuer/pkg/api/v1alpha1"
"k8s.io/apimachinery/pkg/types"
"sync"
"time"
)
const (
// DefaultDurationInterval The default validity duration, if not provided.
DefaultDurationInterval = time.Hour * 24 * 7
// OCICertManagerTagKey The default tag key on a certificate
OCICertManagerTagKey = "cert-manager"
// OCICertManagerTagValue The default tag value on a certificate
OCICertManagerTagValue = "true"
OCICertificatePrivateBundleType = "CERTIFICATE_CONTENT_WITH_PRIVATE_KEY"
)
// GenericProvisioner abstracts over the Provisioner type for mocking purposes
type GenericProvisioner interface {
Sign(ctx context.Context, cr *cmapi.CertificateRequest, log logr.Logger) ([]byte, []byte, error)
}
type ociCAClient interface {
CreateCertificate(ctx context.Context, request certificatesmanagement.CreateCertificateRequest) (response certificatesmanagement.CreateCertificateResponse, err error)
GetCertificateAuthority(ctx context.Context, request certificatesmanagement.GetCertificateAuthorityRequest) (response certificatesmanagement.GetCertificateAuthorityResponse, err error)
}
type ociCertificateClient interface {
GetCertificateBundle(ctx context.Context, request certificates.GetCertificateBundleRequest) (response certificates.GetCertificateBundleResponse, err error)
}
// Collection stores cached Provisioners, stored by namespaced names of the
// issuer.
type Collection struct {
m sync.Map
}
// Store adds a provisioner to the collection.
func (c *Collection) Store(namespacedName types.NamespacedName, provisioner *Provisioner) {
c.m.Store(namespacedName, provisioner)
}
type Provisioner struct {
caClient ociCAClient
certificateClient ociCertificateClient
logger logr.Logger
iss ocicav1alpha1.OCICAClusterIssuer
compartmentID string
tenancyID string
}
func New(logger logr.Logger, iss ocicav1alpha1.OCICAClusterIssuer) (*Provisioner, error) {
configProvider, err := common.ConfigurationProviderFromFileWithProfile("/Users/wfleming/.oci/config", "DEFAULT", "")
if err != nil {
return nil, err
}
caClient, err := certificatesmanagement.NewCertificatesManagementClientWithConfigurationProvider(configProvider)
certClient, err := certificates.NewCertificatesClientWithConfigurationProvider(configProvider)
if err != nil {
return nil, err
}
p := &Provisioner{
logger: logger,
caClient: caClient,
certificateClient: certClient,
iss: iss,
compartmentID: iss.Spec.CompartmentID,
tenancyID: iss.Spec.TenancyID,
}
return p, nil
}
func (p *Provisioner) Validate(ctx context.Context) error {
res, err := p.caClient.GetCertificateAuthority(ctx, certificatesmanagement.GetCertificateAuthorityRequest{
CertificateAuthorityId: common.String(p.iss.Spec.AuthorityID),
})
if err != nil {
p.logger.Error(err, "cant get certificate authority")
return err
}
if res.Id != &p.iss.Spec.AuthorityID {
return fmt.Errorf("cant find the certificate authority")
}
return nil
}
func (p *Provisioner) Sign(ctx context.Context, cr *cmapi.CertificateRequest, log logr.Logger) ([]byte, error) {
_, err := pki.DecodeX509CertificateRequestBytes(cr.Spec.Request)
if err != nil {
return nil, fmt.Errorf("failed to decode CSR for signing: %s", err)
}
var expiry time.Time
start := time.Now().UTC()
if cr.Spec.Duration == nil {
expiry = start.Add(DefaultDurationInterval)
} else {
expiry = start.Add(cr.Spec.Duration.Duration)
}
certificateSignResponse, err := p.caClient.CreateCertificate(ctx, certificatesmanagement.CreateCertificateRequest{
CreateCertificateDetails: certificatesmanagement.CreateCertificateDetails{
Name: &cr.Name,
CompartmentId: &p.iss.Spec.CompartmentID,
CertificateConfig: certificatesmanagement.CreateCertificateManagedExternallyIssuedByInternalCaConfigDetails{
IssuerCertificateAuthorityId: &p.iss.Spec.AuthorityID,
CsrPem: common.String(string(cr.Spec.Request)),
VersionName: &cr.Name,
Validity: &certificatesmanagement.Validity{
TimeOfValidityNotAfter: &common.SDKTime{Time: expiry},
TimeOfValidityNotBefore: &common.SDKTime{Time: start},
},
},
Description: common.String(cr.Name),
FreeformTags: map[string]string{
OCICertManagerTagKey: OCICertManagerTagValue,
},
},
})
if err != nil {
return nil, err
}
res, err := p.certificateClient.GetCertificateBundle(ctx, certificates.GetCertificateBundleRequest{
CertificateId: certificateSignResponse.Id,
CertificateVersionName: common.String(cr.Name),
CertificateBundleType: OCICertificatePrivateBundleType,
})
if err != nil {
p.logger.Error(err, "failed fetching certificate")
return nil, err
}
chainPem := res.GetCertChainPem()
if chainPem != nil {
return nil, fmt.Errorf("failed parsing certificate chain")
}
return []byte(*chainPem), nil
}