Skip to content

Commit

Permalink
add util to self sign cert and update to WebhookConfiguration
Browse files Browse the repository at this point in the history
  • Loading branch information
wu8685 committed Jul 6, 2024
1 parent 110895f commit 075a57f
Show file tree
Hide file tree
Showing 10 changed files with 1,189 additions and 0 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ require (
github.com/onsi/ginkgo v1.16.5
github.com/onsi/gomega v1.30.0
github.com/prometheus/client_golang v1.16.0
github.com/spf13/afero v1.5.1
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.4
github.com/zoumo/golib v0.2.0
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6
k8s.io/api v0.28.4
k8s.io/apimachinery v0.28.4
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
Expand Down Expand Up @@ -332,6 +333,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down Expand Up @@ -387,6 +389,8 @@ github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/afero v1.5.1 h1:VHu76Lk0LSP1x254maIu2bplkWpfBWI+B+6fdoZprcg=
github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
Expand Down Expand Up @@ -420,6 +424,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zoumo/golib v0.2.0 h1:K6W8WWrgnl2bXRvUaiXjAaiFKsCTHwnrBkBHZoFr8lE=
github.com/zoumo/golib v0.2.0/go.mod h1:gOMPRvDgn9m49tfHoKUb2RO0NqplNoe/qj5/ZrczjgQ=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
Expand Down Expand Up @@ -466,6 +472,7 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
Expand Down
134 changes: 134 additions & 0 deletions webhook/cert/cert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/**
* Copyright 2024 The KusionStack 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
*
* https://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 cert

import (
"context"
"crypto/rsa"
"crypto/x509"
"fmt"
"net"
"time"

"github.com/zoumo/golib/cert"
certutil "github.com/zoumo/golib/cert"
)

type (
Config = cert.Config
AltNames = cert.AltNames
)

type ServingCerts struct {
Key []byte
Cert []byte
CAKey []byte
CACert []byte
}

func (c *ServingCerts) Validate(host string) error {
if len(c.Key) == 0 {
return fmt.Errorf("private key is empty")
}
if len(c.Cert) == 0 {
return fmt.Errorf("cetificate is empty")
}
if len(c.CAKey) == 0 {
return fmt.Errorf("CA private key is empty")
}
if len(c.CACert) == 0 {
return fmt.Errorf("CA certificate is empty")
}

tlsCert, err := cert.X509KeyPair(c.Cert, c.Key)
if err != nil {
return fmt.Errorf("invalid x509 keypair: %w", err)
}

// verify cert with ca and host
pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM(c.CACert) {
return fmt.Errorf("no valid CA certificate found")
}

options := x509.VerifyOptions{
Roots: pool,
DNSName: host,
CurrentTime: time.Now(),
}

_, err = tlsCert.X509Cert.Verify(options)
return err
}

func GenerateSelfSignedCerts(cfg Config) (*ServingCerts, error) {
caKey, caCert, key, cert, err := generateSelfSignedCertKey(cfg)
if err != nil {
return nil, err
}

keyPEM := certutil.MarshalRSAPrivateKeyToPEM(key)
cerPEM := certutil.MarshalCertToPEM(cert)
caKeyPEM := certutil.MarshalRSAPrivateKeyToPEM(caKey)
caCertPEM := certutil.MarshalCertToPEM(caCert)

return &ServingCerts{
CAKey: caKeyPEM.EncodeToMemory(),
CACert: caCertPEM.EncodeToMemory(),
Key: keyPEM.EncodeToMemory(),
Cert: cerPEM.EncodeToMemory(),
}, nil
}

func GenerateSelfSignedCertKeyIfNotExist(path string, cfg cert.Config) error {
fscerts, err := NewFSProvider(path, FSOptions{})
if err != nil {
return err
}
return fscerts.Ensure(context.Background(), cfg)
}

func generateSelfSignedCertKey(cfg Config) (*rsa.PrivateKey, *x509.Certificate, *rsa.PrivateKey, *x509.Certificate, error) {
caKey, err := certutil.NewRSAPrivateKey()
if err != nil {
return nil, nil, nil, nil, err
}

caCert, err := certutil.NewSelfSignedCACert(certutil.Config{
CommonName: fmt.Sprintf("%s-ca@%d", cfg.CommonName, time.Now().Unix()),
}, caKey)
if err != nil {
return nil, nil, nil, nil, err
}

key, err := certutil.NewRSAPrivateKey()
if err != nil {
return nil, nil, nil, nil, err
}

if ip := net.ParseIP(cfg.CommonName); ip != nil {
cfg.AltNames.IPs = append(cfg.AltNames.IPs, ip)
} else {
cfg.AltNames.DNSNames = append(cfg.AltNames.DNSNames, cfg.CommonName)
}

cert, err := certutil.NewSignedCert(cfg, key, caKey, caCert)
if err != nil {
return nil, nil, nil, nil, err
}
return caKey, caCert, key, cert, nil
}
55 changes: 55 additions & 0 deletions webhook/cert/cert_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Copyright 2024 The KusionStack 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
*
* https://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 cert

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/zoumo/golib/cert"
)

func TestServingCerts_Validate(t *testing.T) {
cfg := Config{
CommonName: "foo.example.com",
AltNames: cert.AltNames{
DNSNames: []string{"bar.example.com"},
},
}
certs, err := GenerateSelfSignedCerts(cfg)
assert.Nil(t, err)
assert.Nil(t, certs.Validate("foo.example.com"))
assert.Nil(t, certs.Validate("bar.example.com"))
assert.NotNil(t, certs.Validate("unknown.example.com"))
}

func TestGenerateSelfSignedCerts(t *testing.T) {
cfg := Config{
CommonName: "rollout.rollout-system.svc",
AltNames: cert.AltNames{
DNSNames: []string{"rollout.rollout-system.svc", "foo.example.com"},
},
}
certs, err := GenerateSelfSignedCerts(cfg)
assert.Nil(t, err)

err = certs.Validate("rollout.rollout-system.svc")
assert.Nil(t, err)

err = certs.Validate("foo.example.com")
assert.Nil(t, err)
}
38 changes: 38 additions & 0 deletions webhook/cert/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Copyright 2024 The KusionStack 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
*
* https://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 cert

import (
"errors"
"fmt"

apierrors "k8s.io/apimachinery/pkg/api/errors"
)

var errNotFound = errors.New("not found")

func newNotFound(name string, err error) error {
return fmt.Errorf("%s %w: %v", name, errNotFound, err)
}

func IsNotFound(err error) bool {
return apierrors.IsNotFound(err) || errors.Is(err, errNotFound)
}

func IsConflict(err error) bool {
return apierrors.IsAlreadyExists(err) || apierrors.IsConflict(err)
}
34 changes: 34 additions & 0 deletions webhook/cert/error_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Copyright 2024 The KusionStack 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
*
* https://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 cert

import (
"errors"
"testing"

"github.com/stretchr/testify/assert"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime/schema"
)

func TestIsNotFound(t *testing.T) {
err := newNotFound("testFile", errors.New("testError"))
assert.True(t, IsNotFound(err))

err = apierrors.NewNotFound(schema.GroupResource{}, "testResource")
assert.True(t, IsNotFound(err))
}
Loading

0 comments on commit 075a57f

Please sign in to comment.