Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move pkg/certificates from control-protocol to networking #802

Merged
merged 6 commits into from
Jun 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/gorilla/websocket v1.4.2
github.com/hashicorp/golang-lru v0.5.4
github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417
github.com/stretchr/testify v1.8.0
go.uber.org/atomic v1.9.0
go.uber.org/zap v1.19.1
golang.org/x/sync v0.1.0
Expand Down Expand Up @@ -57,6 +58,7 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/openzipkin/zipkin-go v0.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -371,13 +371,16 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
Expand Down
2 changes: 1 addition & 1 deletion pkg/apis/networking/v1alpha1/certificate_lifecycle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func TestMarkReady(t *testing.T) {
}
}

func TestMarkNotReady(t *testing.T) {
func TestMarkNotReady(_ *testing.T) {
c := &CertificateStatus{}
c.InitializeConditions()
apistest.CheckCondition(c, CertificateConditionReady, corev1.ConditionUnknown)
Expand Down
2 changes: 1 addition & 1 deletion pkg/apis/networking/v1alpha1/certificate_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func (c *Certificate) Validate(ctx context.Context) *apis.FieldError {
}

// Validate inspects and validates CertificateSpec object.
func (spec *CertificateSpec) Validate(ctx context.Context) (all *apis.FieldError) {
func (spec *CertificateSpec) Validate(_ context.Context) (all *apis.FieldError) {
// Spec must have at least one DNS Name.
if len(spec.DNSNames) == 0 {
all = all.Also(apis.ErrMissingField("dnsNames"))
Expand Down
2 changes: 1 addition & 1 deletion pkg/apis/networking/v1alpha1/domain_defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ func (d *Domain) SetDefaults(ctx context.Context) {
}

// SetDefaults populates default values in DomainSpec
func (*DomainSpec) SetDefaults(ctx context.Context) {
func (*DomainSpec) SetDefaults(_ context.Context) {
}
4 changes: 2 additions & 2 deletions pkg/apis/networking/v1alpha1/domain_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@ func (ds *DomainSpec) Validate(ctx context.Context) *apis.FieldError {
return all
}

func (lb *LoadBalancerIngressSpec) Validate(ctx context.Context) *apis.FieldError {
func (lb *LoadBalancerIngressSpec) Validate(_ context.Context) *apis.FieldError {
var all *apis.FieldError
if lb.Domain == "" && lb.DomainInternal == "" && lb.IP == "" && !lb.MeshOnly {
return all.Also(apis.ErrMissingOneOf("domain", "domainInternal", "ip", "meshOnly"))
}
return all
}

func (cfg *IngressConfig) Validate(ctx context.Context) *apis.FieldError {
func (cfg *IngressConfig) Validate(_ context.Context) *apis.FieldError {
var all *apis.FieldError
if cfg.Name == "" {
all = all.Also(apis.ErrMissingField("name"))
Expand Down
4 changes: 2 additions & 2 deletions pkg/apis/networking/v1alpha1/ingress_defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (is *IngressSpec) SetDefaults(ctx context.Context) {
}

// SetDefaults populates default values in IngressTLS
func (t *IngressTLS) SetDefaults(ctx context.Context) {}
func (t *IngressTLS) SetDefaults(_ context.Context) {}

// SetDefaults populates default values in IngressRule
func (r *IngressRule) SetDefaults(ctx context.Context) {
Expand All @@ -56,7 +56,7 @@ func (h *HTTPIngressRuleValue) SetDefaults(ctx context.Context) {
}

// SetDefaults populates default values in HTTPIngressPath
func (h *HTTPIngressPath) SetDefaults(ctx context.Context) {
func (h *HTTPIngressPath) SetDefaults(_ context.Context) {
// If only one split is specified, we default to 100.
if len(h.Splits) == 1 && h.Splits[0].Percent == 0 {
h.Splits[0].Percent = 100
Expand Down
4 changes: 2 additions & 2 deletions pkg/apis/networking/v1alpha1/ingress_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ func (b IngressBackend) Validate(ctx context.Context) *apis.FieldError {
}

// Validate inspects and validates IngressTLS object.
func (t *IngressTLS) Validate(ctx context.Context) *apis.FieldError {
func (t *IngressTLS) Validate(_ context.Context) *apis.FieldError {
// Provided TLS setting must not be empty.
if equality.Semantic.DeepEqual(t, &IngressTLS{}) {
return apis.ErrMissingField(apis.CurrentField)
Expand All @@ -168,7 +168,7 @@ func (t *IngressTLS) Validate(ctx context.Context) *apis.FieldError {
return all
}

func (t HTTPOption) Validate(ctx context.Context) (all *apis.FieldError) {
func (t HTTPOption) Validate(_ context.Context) (all *apis.FieldError) {
switch t {
case "", HTTPOptionEnabled, HTTPOptionRedirected:
break
Expand Down
2 changes: 1 addition & 1 deletion pkg/apis/networking/v1alpha1/realm_defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ func (r *Realm) SetDefaults(ctx context.Context) {
}

// SetDefaults populates default values in RealmSpec
func (*RealmSpec) SetDefaults(ctx context.Context) {
func (*RealmSpec) SetDefaults(_ context.Context) {
}
2 changes: 1 addition & 1 deletion pkg/apis/networking/v1alpha1/realm_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func (r *Realm) Validate(ctx context.Context) *apis.FieldError {
}

// Validate inspects and validates RealmSpec object.
func (rs *RealmSpec) Validate(ctx context.Context) *apis.FieldError {
func (rs *RealmSpec) Validate(_ context.Context) *apis.FieldError {
var all *apis.FieldError
if rs.External == "" && rs.Internal == "" {
all = all.Also(apis.ErrMissingOneOf("external", "internal"))
Expand Down
2 changes: 1 addition & 1 deletion pkg/apis/networking/v1alpha1/serverlessservice_defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ func (ss *ServerlessService) SetDefaults(ctx context.Context) {
}

// SetDefaults sets default values on the ServerlessServiceSpec.
func (*ServerlessServiceSpec) SetDefaults(ctx context.Context) {
func (*ServerlessServiceSpec) SetDefaults(_ context.Context) {
// Nothing is defaultable so far.
}
184 changes: 184 additions & 0 deletions pkg/certificates/certs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/*
Copyright 2021 The Knative 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 certificates

import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"math/big"
"time"
)

var randReader = rand.Reader
var serialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128)

// Create template common to all certificates
func createCertTemplate(expirationInterval time.Duration, sans []string) (*x509.Certificate, error) {
serialNumber, err := rand.Int(randReader, serialNumberLimit)
if err != nil {
return nil, fmt.Errorf("failed to generate serial number: %w", err)
}

tmpl := x509.Certificate{
SerialNumber: serialNumber,
SignatureAlgorithm: x509.SHA256WithRSA,
NotBefore: time.Now(),
NotAfter: time.Now().Add(expirationInterval),
BasicConstraintsValid: true,
DNSNames: sans,
}
return &tmpl, nil
}

// Create cert template suitable for CA and hence signing
func createCACertTemplate(expirationInterval time.Duration) (*x509.Certificate, error) {
rootCert, err := createCertTemplate(expirationInterval, []string{})
if err != nil {
return nil, err
}
// Make it into a CA cert and change it so we can use it to sign certs
rootCert.IsCA = true
rootCert.KeyUsage = x509.KeyUsageCertSign
rootCert.Subject = pkix.Name{
Organization: []string{Organization},
}
return rootCert, nil
}

// Create cert template that we can use on the client/server for TLS
func createTransportCertTemplate(expirationInterval time.Duration, sans []string) (*x509.Certificate, error) {
cert, err := createCertTemplate(expirationInterval, sans)
if err != nil {
return nil, err
}
cert.KeyUsage = x509.KeyUsageDigitalSignature
cert.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
cert.Subject = pkix.Name{
Organization: []string{Organization},
CommonName: "control-protocol-certificate",
}
return cert, err
}

func createCert(template, parent *x509.Certificate, pub, parentPriv interface{}) (certPEM *pem.Block, err error) {
certDER, err := x509.CreateCertificate(rand.Reader, template, parent, pub, parentPriv)
if err != nil {
return
}
_, err = x509.ParseCertificate(certDER)
if err != nil {
return
}
certPEM = &pem.Block{Type: "CERTIFICATE", Bytes: certDER}
return
}

// CreateCACerts generates the root CA cert
func CreateCACerts(expirationInterval time.Duration) (*KeyPair, error) {
caKeyPair, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, fmt.Errorf("error generating random key: %w", err)
}

rootCertTmpl, err := createCACertTemplate(expirationInterval)
if err != nil {
return nil, fmt.Errorf("error generating CA cert: %w", err)
}

caCertPem, err := createCert(rootCertTmpl, rootCertTmpl, &caKeyPair.PublicKey, caKeyPair)
if err != nil {
return nil, fmt.Errorf("error signing the CA cert: %w", err)
}
caPrivateKeyPem := &pem.Block{
Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(caKeyPair),
}
return NewKeyPair(caPrivateKeyPem, caCertPem), nil
}

// Deprecated: CreateControlPlaneCert generates the certificate for the client
func CreateControlPlaneCert(_ context.Context, caKey *rsa.PrivateKey, caCertificate *x509.Certificate, expirationInterval time.Duration) (*KeyPair, error) {
return CreateCert(caKey, caCertificate, expirationInterval, LegacyFakeDnsName)
}

// Deprecated: CreateDataPlaneCert generates the certificate for the server
func CreateDataPlaneCert(_ context.Context, caKey *rsa.PrivateKey, caCertificate *x509.Certificate, expirationInterval time.Duration) (*KeyPair, error) {
return CreateCert(caKey, caCertificate, expirationInterval, LegacyFakeDnsName)
}

// CreateCert generates the certificate for use by client and server
func CreateCert(caKey *rsa.PrivateKey, caCertificate *x509.Certificate, expirationInterval time.Duration, sans ...string) (*KeyPair, error) {

// Then create the private key for the serving cert
keyPair, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, fmt.Errorf("error generating random key: %w", err)
}

certTemplate, err := createTransportCertTemplate(expirationInterval, sans)
if err != nil {
return nil, fmt.Errorf("failed to create the certificate template: %w", err)
}

// create a certificate which wraps the public key, sign it with the CA private key
certPEM, err := createCert(certTemplate, caCertificate, &keyPair.PublicKey, caKey)
if err != nil {
return nil, fmt.Errorf("error signing certificate template: %w", err)
}

privateKeyPEM := &pem.Block{
Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(keyPair),
}
return NewKeyPair(privateKeyPEM, certPEM), nil
}

// ParseCert parses a certificate/private key pair from serialized pem blocks
func ParseCert(certPemBytes []byte, privateKeyPemBytes []byte) (*x509.Certificate, *rsa.PrivateKey, error) {
certBlock, _ := pem.Decode(certPemBytes)
if certBlock == nil {
return nil, nil, fmt.Errorf("decoding the cert block returned nil")
}
if certBlock.Type != "CERTIFICATE" {
return nil, nil, fmt.Errorf("bad pem block, expecting type 'CERTIFICATE', found %q", certBlock.Type)
}
cert, err := x509.ParseCertificate(certBlock.Bytes)
if err != nil {
return nil, nil, err
}

pkBlock, _ := pem.Decode(privateKeyPemBytes)
if pkBlock == nil {
return nil, nil, fmt.Errorf("decoding the pk block returned nil")
}
if pkBlock.Type != "RSA PRIVATE KEY" {
return nil, nil, fmt.Errorf("bad pem block, expecting type 'RSA PRIVATE KEY', found %q", pkBlock.Type)
}
pk, err := x509.ParsePKCS1PrivateKey(pkBlock.Bytes)
return cert, pk, err
}

// CheckExpiry checks the expiration of the certificate
func CheckExpiry(cert *x509.Certificate, rotationThreshold time.Duration) error {
if time.Now().Add(rotationThreshold).After(cert.NotAfter) {
return fmt.Errorf("certificate is going to expire %v", cert.NotAfter)
}
return nil
}
60 changes: 60 additions & 0 deletions pkg/certificates/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
Copyright 2021 The Knative 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 certificates

import "strings"

const (
Organization = "knative.dev"
//nolint:all
LegacyFakeDnsName = "data-plane." + Organization
//nolint:all
// Deprecated: FakeDnsName is deprecated. Please use the DataPlaneRoutingName or DataPlaneUserName function.
FakeDnsName = LegacyFakeDnsName
dataPlaneUserPrefix = "kn-user-"
dataPlaneRoutingPrefix = "kn-routing-"
ControlPlaneName = "kn-control"

//These keys are meant to line up with cert-manager, see
//https://cert-manager.io/docs/usage/certificate/#additional-certificate-output-formats
CaCertName = "ca.crt"
CertName = "tls.crt"
PrivateKeyName = "tls.key"

//These should be able to be deprecated some time in the future when the new names are fully adopted
// #nosec
SecretCaCertKey = "ca-cert.pem"
// #nosec
SecretCertKey = "public-cert.pem"
// #nosec
SecretPKKey = "private-key.pem"
)

// DataPlaneRoutingName constructs a san for a data-plane-routing certificate
// Accepts a routingId - a unique identifier used as part of the san (default is "0" used when an empty routingId is provided)
func DataPlaneRoutingName(routingID string) string {
if routingID == "" {
routingID = "0"
}
return dataPlaneRoutingPrefix + strings.ToLower(routingID)
}

// DataPlaneUserName constructs a san for a data-plane-user certificate
// Accepts a namespace - the namespace for which the certificate was created
func DataPlaneUserName(namespace string) string {
return dataPlaneUserPrefix + strings.ToLower(namespace)
}
Loading
Loading