blob: 11fa9938d4eed60f806e9af0a33400262d28cb35 [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 ra
import (
"fmt"
"time"
)
import (
meshconfig "istio.io/api/mesh/v1alpha1"
clientset "k8s.io/client-go/kubernetes"
)
import (
raerror "github.com/apache/dubbo-go-pixiu/security/pkg/pki/error"
"github.com/apache/dubbo-go-pixiu/security/pkg/pki/util"
caserver "github.com/apache/dubbo-go-pixiu/security/pkg/server/ca"
)
// RegistrationAuthority : Registration Authority interface.
type RegistrationAuthority interface {
caserver.CertificateAuthority
// SetCACertificatesFromMeshConfig sets the CACertificates using the ones from mesh config
SetCACertificatesFromMeshConfig([]*meshconfig.MeshConfig_CertificateData)
// GetRootCertFromMeshConfig returns the root cert for the specific signer in mesh config
GetRootCertFromMeshConfig(signerName string) ([]byte, error)
}
// CaExternalType : Type of External CA integration
type CaExternalType string
// IstioRAOptions : Configuration Options for the IstioRA
type IstioRAOptions struct {
// ExternalCAType: Integration API type with external CA
ExternalCAType CaExternalType
// DefaultCertTTL: Default Certificate TTL
DefaultCertTTL time.Duration
// MaxCertTTL: Maximum Certificate TTL that can be requested
MaxCertTTL time.Duration
// CaCertFile : File containing PEM encoded CA root certificate of external CA
CaCertFile string
// CaSigner : To indicate custom CA Signer name when using external K8s CA
CaSigner string
// VerifyAppendCA : Whether to use caCertFile containing CA root cert to verify and append to signed cert-chain
VerifyAppendCA bool
// K8sClient : K8s API client
K8sClient clientset.Interface
// TrustDomain
TrustDomain string
// CertSignerDomain info
CertSignerDomain string
}
const (
// ExtCAK8s : Integrate with external CA using k8s CSR API
ExtCAK8s CaExternalType = "ISTIOD_RA_KUBERNETES_API"
// ExtCAGrpc : Integration with external CA using Istio CA gRPC API
ExtCAGrpc CaExternalType = "ISTIOD_RA_ISTIO_API"
// DefaultExtCACertDir : Location of external CA certificate
DefaultExtCACertDir string = "./etc/external-ca-cert"
)
// ValidateCSR : Validate all SAN extensions in csrPEM match authenticated identities
func ValidateCSR(csrPEM []byte, subjectIDs []string) bool {
var match bool
csr, err := util.ParsePemEncodedCSR(csrPEM)
if err != nil {
return false
}
csrIDs, err := util.ExtractIDs(csr.Extensions)
if err != nil {
return false
}
for _, s1 := range csrIDs {
match = false
for _, s2 := range subjectIDs {
if s1 == s2 {
match = true
break
}
}
if !match {
return false
}
}
return true
}
// NewIstioRA is a factory method that returns an RA that implements the RegistrationAuthority functionality.
// the caOptions defines the external provider
func NewIstioRA(opts *IstioRAOptions) (RegistrationAuthority, error) {
if opts.ExternalCAType == ExtCAK8s {
istioRA, err := NewKubernetesRA(opts)
if err != nil {
return nil, fmt.Errorf("failed to create an K8s CA: %v", err)
}
return istioRA, err
}
return nil, fmt.Errorf("invalid CA Name %s", opts.ExternalCAType)
}
// preSign : Validation checks to execute before signing certificates
func preSign(raOpts *IstioRAOptions, csrPEM []byte, subjectIDs []string, requestedLifetime time.Duration, forCA bool) (time.Duration, error) {
if forCA {
return requestedLifetime, raerror.NewError(raerror.CSRError,
fmt.Errorf("unable to generate CA certifificates"))
}
if !ValidateCSR(csrPEM, subjectIDs) {
return requestedLifetime, raerror.NewError(raerror.CSRError, fmt.Errorf(
"unable to validate SAN Identities in CSR"))
}
// If the requested requestedLifetime is non-positive, apply the default TTL.
lifetime := requestedLifetime
if requestedLifetime.Seconds() <= 0 {
lifetime = raOpts.DefaultCertTTL
}
// If the requested TTL is greater than maxCertTTL, return an error
if requestedLifetime.Seconds() > raOpts.MaxCertTTL.Seconds() {
return lifetime, raerror.NewError(raerror.TTLError, fmt.Errorf(
"requested TTL %s is greater than the max allowed TTL %s", requestedLifetime, raOpts.MaxCertTTL))
}
return lifetime, nil
}