Skip to content

Commit e3eba9e

Browse files
committed
refactor: introduce unified PKI interface for Fabric CA and Vault
- Added a new `pki` package to provide a unified interface for PKI operations. - Deprecated the existing `certs` and `certs_vault` packages, with migration guides included. - Implemented `pkiHelper` for managing PKI operations in the identity, orderer node, and peer controllers. - Updated controllers to utilize the new PKI interface for certificate enrollment and renewal processes. - Added provider registration for both Fabric CA and Vault in the new PKI architecture. Signed-off-by: Aditya Joshi <[email protected]>
1 parent 7443572 commit e3eba9e

File tree

13 files changed

+2312
-36
lines changed

13 files changed

+2312
-36
lines changed

controllers/certs/provision_certs.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
// Package certs provides PKI operations using Fabric CA.
2+
//
3+
// Deprecated: This package is deprecated and will be removed in a future version.
4+
// Use the github.com/kfsoftware/hlf-operator/pkg/pki package instead.
5+
// The new PKI package provides a unified interface for both Fabric CA and HashiCorp Vault.
6+
//
7+
// Migration guide:
8+
//
9+
// Old code:
10+
// crt, key, rootCrt, err := certs.EnrollUser(certs.EnrollUserRequest{...})
11+
//
12+
// New code:
13+
// import "github.com/kfsoftware/hlf-operator/pkg/pki"
14+
// import _ "github.com/kfsoftware/hlf-operator/pkg/pki/fabricca"
15+
//
16+
// provider, _ := pki.NewProvider(&pki.ProviderConfig{
17+
// Type: pki.ProviderTypeFabricCA,
18+
// FabricCA: &pki.FabricCAConfig{...},
19+
// })
20+
// resp, err := provider.Enroll(ctx, pki.EnrollRequest{...})
121
package certs
222

323
import (

controllers/certs_vault/provision_certs.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
// Package certs_vault provides PKI operations using HashiCorp Vault.
2+
//
3+
// Deprecated: This package is deprecated and will be removed in a future version.
4+
// Use the github.com/kfsoftware/hlf-operator/pkg/pki package instead.
5+
// The new PKI package provides a unified interface for both Fabric CA and HashiCorp Vault.
6+
//
7+
// Migration guide:
8+
//
9+
// Old code:
10+
// crt, key, rootCrt, err := certs_vault.EnrollUser(clientSet, vaultConf, request, params)
11+
//
12+
// New code:
13+
// import "github.com/kfsoftware/hlf-operator/pkg/pki"
14+
// import _ "github.com/kfsoftware/hlf-operator/pkg/pki/vault"
15+
//
16+
// provider, _ := pki.NewProvider(&pki.ProviderConfig{
17+
// Type: pki.ProviderTypeVault,
18+
// ClientSet: clientSet,
19+
// Vault: &pki.VaultConfig{...},
20+
// })
21+
// resp, err := provider.Enroll(ctx, pki.EnrollRequest{...})
122
package certs_vault
223

324
import (
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
package identity
2+
3+
import (
4+
"context"
5+
"crypto/ecdsa"
6+
"crypto/x509"
7+
"encoding/base64"
8+
"fmt"
9+
10+
hlfv1alpha1 "github.com/kfsoftware/hlf-operator/pkg/apis/hlf.kungfusoftware.es/v1alpha1"
11+
"github.com/kfsoftware/hlf-operator/pkg/pki"
12+
"github.com/pkg/errors"
13+
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14+
"k8s.io/client-go/kubernetes"
15+
)
16+
17+
// pkiHelper provides PKI operations for the identity controller using the unified PKI interface
18+
type pkiHelper struct {
19+
clientSet kubernetes.Interface
20+
}
21+
22+
// newPKIHelper creates a new PKI helper
23+
func newPKIHelper(clientSet kubernetes.Interface) *pkiHelper {
24+
return &pkiHelper{clientSet: clientSet}
25+
}
26+
27+
// createProvider creates the appropriate PKI provider based on credential store configuration
28+
func (h *pkiHelper) createProvider(ctx context.Context, conf *hlfv1alpha1.FabricIdentity) (pki.Provider, error) {
29+
switch conf.Spec.CredentialStore {
30+
case hlfv1alpha1.CredentialStoreVault:
31+
return h.createVaultProvider(ctx, conf)
32+
default:
33+
return h.createFabricCAProvider(ctx, conf)
34+
}
35+
}
36+
37+
// createFabricCAProvider creates a FabricCA PKI provider from identity config
38+
func (h *pkiHelper) createFabricCAProvider(ctx context.Context, conf *hlfv1alpha1.FabricIdentity) (pki.Provider, error) {
39+
cacert, err := h.getCertBytesFromCATLS(conf.Spec.Catls)
40+
if err != nil {
41+
return nil, err
42+
}
43+
44+
caURL := fmt.Sprintf("https://%s:%d", conf.Spec.Cahost, conf.Spec.Caport)
45+
46+
return pki.NewProvider(&pki.ProviderConfig{
47+
Type: pki.ProviderTypeFabricCA,
48+
ClientSet: h.clientSet,
49+
FabricCA: &pki.FabricCAConfig{
50+
URL: caURL,
51+
CAName: conf.Spec.Caname,
52+
TLSCert: string(cacert),
53+
MSPID: conf.Spec.MSPID,
54+
},
55+
})
56+
}
57+
58+
// createVaultProvider creates a Vault PKI provider from identity config
59+
func (h *pkiHelper) createVaultProvider(ctx context.Context, conf *hlfv1alpha1.FabricIdentity) (pki.Provider, error) {
60+
if conf.Spec.Vault == nil {
61+
return nil, errors.New("vault configuration is required for vault credential store")
62+
}
63+
64+
vaultConf := &conf.Spec.Vault.Vault
65+
vaultReq := &conf.Spec.Vault.Request
66+
67+
return pki.NewProvider(&pki.ProviderConfig{
68+
Type: pki.ProviderTypeVault,
69+
ClientSet: h.clientSet,
70+
Vault: &pki.VaultConfig{
71+
URL: vaultConf.URL,
72+
PKIPath: vaultReq.PKI,
73+
Role: vaultReq.Role,
74+
TTL: vaultReq.TTL,
75+
Auth: pki.VaultAuthConfig{
76+
TokenSecretRef: convertSecretRef(vaultConf.TokenSecretRef),
77+
},
78+
TLS: pki.VaultTLSConfig{
79+
CACert: vaultConf.CACert,
80+
ClientCert: vaultConf.ClientCert,
81+
ClientKeySecretRef: convertSecretRef(vaultConf.ClientKeySecretRef),
82+
ServerName: vaultConf.ServerName,
83+
SkipVerify: vaultConf.TLSSkipVerify,
84+
},
85+
},
86+
})
87+
}
88+
89+
// CreateSignCryptoMaterialV2 creates sign crypto material using the PKI interface
90+
func (h *pkiHelper) CreateSignCryptoMaterialV2(
91+
ctx context.Context,
92+
conf *hlfv1alpha1.FabricIdentity,
93+
) (*x509.Certificate, *ecdsa.PrivateKey, *x509.Certificate, error) {
94+
provider, err := h.createProvider(ctx, conf)
95+
if err != nil {
96+
return nil, nil, nil, errors.Wrap(err, "failed to create PKI provider")
97+
}
98+
99+
resp, err := provider.Enroll(ctx, pki.EnrollRequest{
100+
User: conf.Spec.Enrollid,
101+
Secret: conf.Spec.Enrollsecret,
102+
CN: conf.Name,
103+
Hosts: []string{},
104+
MSPID: conf.Spec.MSPID,
105+
Profile: "",
106+
})
107+
if err != nil {
108+
return nil, nil, nil, errors.Wrap(err, "failed to enroll identity")
109+
}
110+
111+
return resp.Certificate, resp.PrivateKey, resp.RootCertificate, nil
112+
}
113+
114+
// ReenrollSignCryptoMaterialV2 re-enrolls sign crypto material using the PKI interface
115+
func (h *pkiHelper) ReenrollSignCryptoMaterialV2(
116+
ctx context.Context,
117+
conf *hlfv1alpha1.FabricIdentity,
118+
existingCert string,
119+
existingKey *ecdsa.PrivateKey,
120+
) (*x509.Certificate, *ecdsa.PrivateKey, *x509.Certificate, error) {
121+
provider, err := h.createProvider(ctx, conf)
122+
if err != nil {
123+
return nil, nil, nil, errors.Wrap(err, "failed to create PKI provider")
124+
}
125+
126+
resp, err := provider.Reenroll(ctx, pki.ReenrollRequest{
127+
EnrollID: conf.Spec.Enrollid,
128+
CN: conf.Name,
129+
Hosts: []string{},
130+
MSPID: conf.Spec.MSPID,
131+
Profile: "",
132+
ExistingCert: existingCert,
133+
ExistingKey: existingKey,
134+
})
135+
if err != nil {
136+
return nil, nil, nil, errors.Wrap(err, "failed to re-enroll identity")
137+
}
138+
139+
return resp.Certificate, existingKey, resp.RootCertificate, nil
140+
}
141+
142+
// RegisterUserV2 registers a user using the PKI interface
143+
func (h *pkiHelper) RegisterUserV2(
144+
ctx context.Context,
145+
conf *hlfv1alpha1.FabricIdentity,
146+
attributes []pki.Attribute,
147+
) (string, error) {
148+
provider, err := h.createProvider(ctx, conf)
149+
if err != nil {
150+
return "", errors.Wrap(err, "failed to create PKI provider")
151+
}
152+
153+
// Check if provider supports registration
154+
if supporter, ok := provider.(pki.RegistrationSupporter); ok {
155+
if !supporter.SupportsRegistration() {
156+
return "", errors.Errorf("provider %s does not support identity registration", provider.Type())
157+
}
158+
}
159+
160+
resp, err := provider.Register(ctx, pki.RegisterRequest{
161+
EnrollID: conf.Spec.Register.Enrollid,
162+
EnrollSecret: conf.Spec.Register.Enrollsecret,
163+
User: conf.Spec.Enrollid,
164+
Secret: conf.Spec.Enrollsecret,
165+
Type: conf.Spec.Register.Type,
166+
MSPID: conf.Spec.MSPID,
167+
Attributes: attributes,
168+
})
169+
if err != nil {
170+
return "", errors.Wrap(err, "failed to register identity")
171+
}
172+
173+
return resp.Secret, nil
174+
}
175+
176+
// getCertBytesFromCATLS retrieves the CA TLS certificate bytes
177+
func (h *pkiHelper) getCertBytesFromCATLS(caTls *hlfv1alpha1.Catls) ([]byte, error) {
178+
var certBytes []byte
179+
var err error
180+
181+
if caTls.Cacert != "" {
182+
certBytes, err = base64.StdEncoding.DecodeString(caTls.Cacert)
183+
if err != nil {
184+
return nil, errors.Wrap(err, "failed to decode CA cert from base64")
185+
}
186+
} else if caTls.SecretRef != nil {
187+
secret, err := h.clientSet.CoreV1().Secrets(caTls.SecretRef.Namespace).Get(
188+
context.Background(),
189+
caTls.SecretRef.Name,
190+
v1.GetOptions{},
191+
)
192+
if err != nil {
193+
return nil, errors.Wrap(err, "failed to get CA cert secret")
194+
}
195+
certBytes = secret.Data[caTls.SecretRef.Key]
196+
} else {
197+
return nil, errors.New("invalid CA TLS configuration: neither cacert nor secretRef provided")
198+
}
199+
200+
return certBytes, nil
201+
}
202+
203+
// convertSecretRef converts from hlfv1alpha1.VaultSecretRef to pki.SecretRef
204+
func convertSecretRef(ref *hlfv1alpha1.VaultSecretRef) *pki.SecretRef {
205+
if ref == nil {
206+
return nil
207+
}
208+
return &pki.SecretRef{
209+
Namespace: ref.Namespace,
210+
Name: ref.Name,
211+
Key: ref.Key,
212+
}
213+
}

controllers/ordnode/ordnode_controller.go

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ import (
4343
"sigs.k8s.io/controller-runtime/pkg/client"
4444
"sigs.k8s.io/controller-runtime/pkg/controller"
4545
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
46+
47+
// Register PKI providers
48+
_ "github.com/kfsoftware/hlf-operator/pkg/pki/fabricca"
49+
_ "github.com/kfsoftware/hlf-operator/pkg/pki/vault"
4650
)
4751

4852
// FabricOrdererNodeReconciler reconciles a FabricOrdererNode object
@@ -1087,6 +1091,10 @@ func getConfig(
10871091
var tlsKey, adminKey, signKey *ecdsa.PrivateKey
10881092
var err error
10891093
ctx := context.Background()
1094+
1095+
// Create PKI helper for certificate operations
1096+
pki := newPKIHelper(client)
1097+
10901098
if tlsParams.External != nil {
10911099
secret, err := client.CoreV1().Secrets(tlsParams.External.SecretNamespace).Get(ctx, tlsParams.External.SecretName, v1.GetOptions{})
10921100
if err != nil {
@@ -1109,8 +1117,8 @@ func getConfig(
11091117
if err != nil {
11101118
return nil, errors.Wrapf(err, "failed to get existing tls crypto material")
11111119
}
1112-
tlsCert, tlsKey, tlsRootCert, err = ReenrollTLSCryptoMaterial(
1113-
client,
1120+
tlsCert, tlsKey, tlsRootCert, err = pki.ReenrollTLSCryptoMaterialV2(
1121+
ctx,
11141122
conf,
11151123
&tlsParams,
11161124
string(utils.EncodeX509Certificate(tlsCert)),
@@ -1124,8 +1132,8 @@ func getConfig(
11241132
tlsCert, tlsKey, tlsRootCert, err = getExistingTLSCrypto(client, chartName, namespace)
11251133
if err != nil {
11261134
log.Warnf("Failed to get existing tls crypto material for %s, will create new one", chartName)
1127-
tlsCert, tlsKey, tlsRootCert, err = CreateTLSCryptoMaterial(
1128-
client,
1135+
tlsCert, tlsKey, tlsRootCert, err = pki.CreateTLSCryptoMaterialV2(
1136+
ctx,
11291137
conf,
11301138
&tlsParams,
11311139
)
@@ -1139,8 +1147,8 @@ func getConfig(
11391147
if err != nil {
11401148
return nil, errors.Wrapf(err, "failed to get existing tls admin crypto material")
11411149
}
1142-
adminCert, adminKey, adminRootCert, adminClientRootCert, err = ReenrollTLSAdminCryptoMaterial(
1143-
client,
1150+
adminCert, adminKey, adminRootCert, adminClientRootCert, err = pki.ReenrollTLSAdminCryptoMaterialV2(
1151+
ctx,
11441152
conf,
11451153
&tlsParams,
11461154
string(utils.EncodeX509Certificate(adminCert)),
@@ -1153,8 +1161,8 @@ func getConfig(
11531161
adminCert, adminKey, adminRootCert, adminClientRootCert, err = getExistingTLSAdminCrypto(client, chartName, namespace)
11541162
if err != nil {
11551163
log.Warnf("Failed to get existing tls admin crypto material, creating new one")
1156-
adminCert, adminKey, adminRootCert, adminClientRootCert, err = CreateTLSAdminCryptoMaterial(
1157-
client,
1164+
adminCert, adminKey, adminRootCert, adminClientRootCert, err = pki.CreateTLSAdminCryptoMaterialV2(
1165+
ctx,
11581166
conf,
11591167
&tlsParams,
11601168
)
@@ -1187,8 +1195,8 @@ func getConfig(
11871195
return nil, errors.Wrapf(err, "failed to get existing sign crypto material")
11881196
}
11891197
signCertPem := utils.EncodeX509Certificate(signCert)
1190-
signCert, signKey, signRootCert, err = ReenrollSignCryptoMaterial(
1191-
client,
1198+
signCert, signKey, signRootCert, err = pki.ReenrollSignCryptoMaterialV2(
1199+
ctx,
11921200
conf,
11931201
&signParams,
11941202
string(signCertPem),
@@ -1203,8 +1211,8 @@ func getConfig(
12031211
if err != nil {
12041212
log.Warnf("Failed to get existing sign crypto material: %s", err)
12051213

1206-
signCert, signKey, signRootCert, err = CreateSignCryptoMaterial(
1207-
client,
1214+
signCert, signKey, signRootCert, err = pki.CreateSignCryptoMaterialV2(
1215+
ctx,
12081216
conf,
12091217
&signParams,
12101218
)

0 commit comments

Comments
 (0)