blob: 636f475ab9569fd325a79e25be2cea3761db699e [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.
// An example implementation of a CSR Controller.
package csrctrl
import (
"context"
"fmt"
"time"
)
import (
"istio.io/pkg/log"
capi "k8s.io/api/certificates/v1"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
)
import (
"github.com/apache/dubbo-go-pixiu/pkg/test/csrctrl/signer"
"github.com/apache/dubbo-go-pixiu/security/pkg/k8s/chiron"
"github.com/apache/dubbo-go-pixiu/security/pkg/pki/util"
)
// CertificateSigningRequestSigningReconciler reconciles a CertificateSigningRequest object
type CertificateSigningRequestSigningReconciler struct {
client.Client
SignerRoot string
CtrlCertTTL time.Duration
Scheme *runtime.Scheme
SignerNames []string
Signers map[string]*signer.Signer
appendRootCert bool
}
// +kubebuilder:rbac:groups=certificates.k8s.io,resources=certificatesigningrequests,verbs=get;list;watch
// +kubebuilder:rbac:groups=certificates.k8s.io,resources=certificatesigningrequests/status,verbs=patch
// +kubebuilder:rbac:groups=core,resources=events,verbs=create;patch
func (r *CertificateSigningRequestSigningReconciler) Reconcile(_ context.Context, req ctrl.Request) (ctrl.Result, error) {
var csr capi.CertificateSigningRequest
if err := r.Client.Get(context.TODO(), req.NamespacedName, &csr); err != nil {
return ctrl.Result{}, fmt.Errorf("error %q getting CSR", err)
}
var exist bool
_, exist = r.Signers[csr.Spec.SignerName]
switch {
case !csr.DeletionTimestamp.IsZero():
log.Info("CSR has been deleted. Ignoring.")
case csr.Spec.SignerName == "":
log.Info("CSR does not have a signer name. Ignoring.")
case !exist:
log.Infof("CSR signer name does not match. Ignoring.", "signer-name: %s", csr.Spec.SignerName)
case csr.Status.Certificate != nil:
log.Info("CSR has already been signed. Ignoring.")
case IsCertificateRequestApproved(&csr):
log.Info("CSR is not approved, Ignoring.")
default:
log.Info("Signing")
x509cr, err := util.ParsePemEncodedCSR(csr.Spec.Request)
if err != nil {
log.Infof("unable to parse csr: %v", err)
return ctrl.Result{}, nil
}
var ok bool
var signer *signer.Signer
if signer, ok = r.Signers[csr.Spec.SignerName]; !ok {
return ctrl.Result{}, fmt.Errorf("error no signer can sign this csr: %v", err)
}
requestedLifeTime := signer.CertTTL
requestedDuration, ok := csr.Annotations[chiron.RequestLifeTimeAnnotationForCertManager]
if ok {
duration, err := time.ParseDuration(requestedDuration)
if err == nil {
requestedLifeTime = duration
}
}
cert, err := signer.Sign(x509cr, csr.Spec.Usages, requestedLifeTime, r.appendRootCert)
if err != nil {
return ctrl.Result{}, fmt.Errorf("error auto signing csr: %v", err)
}
patch := client.MergeFrom(csr.DeepCopy())
csr.Status.Certificate = cert
if err := r.Client.Status().Patch(context.TODO(), &csr, patch); err != nil {
return ctrl.Result{}, fmt.Errorf("error patching CSR: %v", err)
}
log.Infof("The CSR [%s] has been signed", csr.Spec.SignerName)
}
return ctrl.Result{}, nil
}
func (r *CertificateSigningRequestSigningReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&capi.CertificateSigningRequest{}).
Complete(r)
}
// IsCertificateRequestApproved returns true if a certificate request has the
// "Approved" condition and no "Denied" conditions; false otherwise.
func IsCertificateRequestApproved(csr *capi.CertificateSigningRequest) bool {
approved, denied := GetCertApprovalCondition(&csr.Status)
return approved && !denied
}
func GetCertApprovalCondition(status *capi.CertificateSigningRequestStatus) (approved bool, denied bool) {
for _, c := range status.Conditions {
if c.Type == capi.CertificateApproved {
approved = true
}
if c.Type == capi.CertificateDenied {
denied = true
}
}
return
}