blob: d3d267871dd3ca4a3ccfdbb7100097a6847a3526 [file] [log] [blame]
// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package signer
import (
"bytes"
"crypto"
"fmt"
"os"
"sync/atomic"
"time"
)
import (
"k8s.io/client-go/util/cert"
"k8s.io/client-go/util/keyutil"
)
import (
"github.com/apache/dubbo-go-pixiu/pkg/test/cert/ca"
"github.com/apache/dubbo-go-pixiu/pkg/test/csrctrl/authority"
)
func newCAProvider(signerRoot, signerName string) (*caProvider, error) {
strRoot := signerRoot + "/" + signerName + "/"
// create the folder if it does not exist
if _, err := os.Stat(strRoot); os.IsNotExist(err) {
dErr := os.MkdirAll(strRoot, os.ModePerm)
if dErr != nil {
return nil, fmt.Errorf("error creating CA cert folder %s: %v", strRoot, dErr)
}
cErr := os.Chmod(strRoot, os.ModePerm)
if cErr != nil {
return nil, fmt.Errorf("error change mode of CA cert folder %s: %v", strRoot, cErr)
}
}
caLoader, err := ca.NewRoot(strRoot)
if err != nil {
return nil, fmt.Errorf("error reading CA cert file %s: %v", strRoot, err)
}
// Create the new extensions config for the CA
caConfig, err := ca.NewIstioConfig("dubbo-system")
if err != nil {
return nil, err
}
intermediateCA, err := ca.NewIntermediate(strRoot, caConfig, caLoader)
if err != nil {
return nil, err
}
ret := &caProvider{
caLoader: caLoader,
caIntermediate: intermediateCA,
}
if err := ret.setCA(); err != nil {
return nil, err
}
return ret, nil
}
type caProvider struct {
caValue atomic.Value
caLoader ca.Root
caIntermediate ca.Intermediate
}
// currentCertContent retrieve current certificate content from cert file
func (p *caProvider) currentCertContent() ([]byte, error) {
certBytes, err := os.ReadFile(p.caIntermediate.CertFile)
if err != nil {
return []byte(""), fmt.Errorf("error reading CA from cert file %s: %v", p.caLoader.CertFile, err)
}
return certBytes, nil
}
// currentKeyContent retrieve current private key content from key file
func (p *caProvider) currentKeyContent() ([]byte, error) {
keyBytes, err := os.ReadFile(p.caIntermediate.KeyFile)
if err != nil {
return []byte(""), fmt.Errorf("error reading private key from key file %s: %v", p.caLoader.KeyFile, err)
}
return keyBytes, nil
}
// setCA unconditionally stores the current cert/key content
func (p *caProvider) setCA() error {
certPEM, cerr := p.currentCertContent()
if cerr != nil {
return cerr
}
keyPEM, kerr := p.currentKeyContent()
if kerr != nil {
return kerr
}
certs, err := cert.ParseCertsPEM(certPEM)
if err != nil {
return fmt.Errorf("error reading CA cert file %q: %v", p.caLoader.CertFile, err)
}
if len(certs) != 1 {
return fmt.Errorf("error reading CA cert file %q: expected 1 certificate, found %d", p.caLoader.CertFile, len(certs))
}
key, err := keyutil.ParsePrivateKeyPEM(keyPEM)
if err != nil {
return fmt.Errorf("error reading CA key file %q: %v", p.caLoader.KeyFile, err)
}
priv, ok := key.(crypto.Signer)
if !ok {
return fmt.Errorf("error reading CA key file %q: key did not implement crypto.Signer", p.caLoader.KeyFile)
}
ca := &authority.CertificateAuthority{
RawCert: certPEM,
RawKey: keyPEM,
Certificate: certs[0],
PrivateKey: priv,
Backdate: 5 * time.Minute,
}
p.caValue.Store(ca)
return nil
}
// currentCA provides the current value of the CA.
// It always check for a stale value. This is cheap because it's all an in memory cache of small slices.
func (p *caProvider) currentCA() (*authority.CertificateAuthority, error) {
certPEM, cerr := p.currentCertContent()
if cerr != nil {
return nil, cerr
}
keyPEM, kerr := p.currentKeyContent()
if kerr != nil {
return nil, kerr
}
currCA := p.caValue.Load().(*authority.CertificateAuthority)
if bytes.Equal(currCA.RawCert, certPEM) && bytes.Equal(currCA.RawKey, keyPEM) {
return currCA, nil
}
// the bytes weren't equal, so we have to set and then load
if err := p.setCA(); err != nil {
return currCA, err
}
return p.caValue.Load().(*authority.CertificateAuthority), nil
}