Last active
May 5, 2020 15:07
-
-
Save mjudeikis/4c0fc47552897bf13e82414b7d8a9f28 to your computer and use it in GitHub Desktop.
APIServer certs bug
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"crypto" | |
"crypto/ecdsa" | |
"crypto/elliptic" | |
"crypto/rand" | |
"crypto/rsa" | |
"crypto/sha1" | |
"crypto/x509" | |
"crypto/x509/pkix" | |
"encoding/asn1" | |
"encoding/pem" | |
"fmt" | |
"log" | |
"math" | |
"math/big" | |
"net" | |
"net/http" | |
"time" | |
"io/ioutil" | |
"github.com/pkg/errors" | |
) | |
func HelloServer(w http.ResponseWriter, req *http.Request) { | |
fmt.Println("test") | |
w.Header().Set("Content-Type", "text/plain") | |
w.Write([]byte("Hello from the dark side.\n")) | |
} | |
func main() { | |
fmt.Println("start") | |
err := genCA() | |
if err != nil { | |
panic(err) | |
} | |
http.HandleFunc("/readyz", HelloServer) | |
err = http.ListenAndServeTLS(":8443", "kube-apiserver-internal-lb-server.crt", "kube-apiserver-internal-lb-server.key", nil) | |
if err != nil { | |
log.Fatal("ListenAndServe: ", err) | |
} | |
} | |
const ( | |
keySize = 2048 | |
// Validity4eDay sets the validity of a cert to 96 hours. | |
Validity4Day = time.Hour * 96 | |
// Validity4Year sets the validity of a cert to 4 year. | |
Validity4Year = Validity4Day * 365 | |
// ValidityTenYears sets the validity of a cert to 10 years. | |
ValidityTenYears = Validity4Year * 3 | |
) | |
type CertCfg struct { | |
DNSNames []string | |
ExtKeyUsages []x509.ExtKeyUsage | |
IPAddresses []net.IP | |
KeyUsages x509.KeyUsage | |
Subject pkix.Name | |
Validity time.Duration | |
IsCA bool | |
} | |
type rsaPublicKey struct { | |
N *big.Int | |
E int | |
} | |
// certs in original use case: | |
// Issuer: OU = openshift, CN = kube-apiserver-lb-signer | |
// Subject: O = kube-master, CN = system:kube-apiserver | |
func genCA() error { | |
cfg := &CertCfg{ | |
Subject: pkix.Name{CommonName: "kube-apiserver-lb-signer", OrganizationalUnit: []string{"openshift"}}, | |
KeyUsages: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, | |
Validity: ValidityTenYears, | |
IsCA: true, | |
} | |
return generate(cfg, "kube-apiserver-lb-signer") | |
} | |
func generateCert(crt *rsa.PrivateKey, key *x509.Certificate) error { | |
cfg := &CertCfg{ | |
Subject: pkix.Name{CommonName: "system:kube-apiserver", Organization: []string{"kube-master"}}, | |
KeyUsages: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, | |
ExtKeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, | |
Validity: Validity4Year, | |
DNSNames: []string{ | |
"10.77.170.11", | |
}, | |
} | |
return generateSigned(cfg, crt, key, "kube-apiserver-internal-lb-server") | |
} | |
func generateSigned(cfg *CertCfg, caKey *rsa.PrivateKey, caCert *x509.Certificate, filenameBase string) error { | |
key, crt, err := generateSignedCertificate(caKey, caCert, cfg) | |
if err != nil { | |
return errors.Wrap(err, "failed to generate signed cert/key pair") | |
} | |
writeFiles(filenameBase, key, crt) | |
return nil | |
} | |
func generate(cfg *CertCfg, filenameBase string) error { | |
key, crt, err := generateSelfSignedCertificate(cfg) | |
if err != nil { | |
return errors.Wrap(err, "failed to generate self-signed cert/key pair") | |
} | |
err = generateCert(key, crt) | |
if err != nil { | |
return err | |
} | |
return writeFiles(filenameBase, key, crt) | |
} | |
func writeFiles(fileBase string, key *rsa.PrivateKey, crt *x509.Certificate) error { | |
err := ioutil.WriteFile(fileBase+".key", PrivateKeyToPem(key), 0644) | |
if err != nil { | |
return err | |
} | |
return ioutil.WriteFile(fileBase+".crt", CertToPem(crt), 0644) | |
} | |
// GenerateSignedCertificate generate a key and cert defined by CertCfg and signed by CA. | |
func generateSignedCertificate(caKey *rsa.PrivateKey, caCert *x509.Certificate, | |
cfg *CertCfg) (*rsa.PrivateKey, *x509.Certificate, error) { | |
// create a private key | |
key, err := PrivateKey() | |
if err != nil { | |
return nil, nil, errors.Wrap(err, "failed to generate private key") | |
} | |
// create a CSR | |
csrTmpl := x509.CertificateRequest{Subject: cfg.Subject, DNSNames: cfg.DNSNames, IPAddresses: cfg.IPAddresses} | |
csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &csrTmpl, key) | |
if err != nil { | |
return nil, nil, errors.Wrap(err, "failed to create certificate request") | |
} | |
csr, err := x509.ParseCertificateRequest(csrBytes) | |
if err != nil { | |
return nil, nil, errors.Wrap(err, "error parsing x509 certificate request") | |
} | |
// create a cert | |
cert, err := SignedCertificate(cfg, csr, key, caCert, caKey) | |
if err != nil { | |
return nil, nil, errors.Wrap(err, "failed to create a signed certificate") | |
} | |
return key, cert, nil | |
} | |
// GenerateSelfSignedCertificate generates a key/cert pair defined by CertCfg. | |
func generateSelfSignedCertificate(cfg *CertCfg) (*rsa.PrivateKey, *x509.Certificate, error) { | |
key, err := PrivateKey() | |
if err != nil { | |
return nil, nil, errors.Wrap(err, "failed to generate private key") | |
} | |
crt, err := SelfSignedCertificate(cfg, key) | |
if err != nil { | |
return nil, nil, errors.Wrap(err, "failed to create self-signed certificate") | |
} | |
return key, crt, nil | |
} | |
// PrivateKey generates an RSA Private key and returns the value | |
func PrivateKey() (*rsa.PrivateKey, error) { | |
rsaKey, err := rsa.GenerateKey(rand.Reader, keySize) | |
if err != nil { | |
return nil, errors.Wrap(err, "error generating RSA private key") | |
} | |
return rsaKey, nil | |
} | |
func SignedCertificate( | |
cfg *CertCfg, | |
csr *x509.CertificateRequest, | |
key *rsa.PrivateKey, | |
caCert *x509.Certificate, | |
caKey *rsa.PrivateKey, | |
) (*x509.Certificate, error) { | |
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) | |
serial, err := rand.Int(rand.Reader, serialNumberLimit) | |
if err != nil { | |
return nil, err | |
} | |
certTmpl := x509.Certificate{ | |
DNSNames: csr.DNSNames, | |
ExtKeyUsage: cfg.ExtKeyUsages, | |
IPAddresses: csr.IPAddresses, | |
KeyUsage: cfg.KeyUsages, | |
NotAfter: time.Now().Add(cfg.Validity), | |
NotBefore: time.Now().AddDate(0, 0, -1), | |
SerialNumber: serial, | |
Subject: csr.Subject, | |
IsCA: cfg.IsCA, | |
Version: 3, | |
BasicConstraintsValid: true, | |
} | |
// BADCONFIG | |
pub := caCert.PublicKey.(*rsa.PublicKey) | |
// GOODCONFIG | |
pub := key.Public() | |
//certTmpl.AuthorityKeyId, err = generateSubjectKeyID(pub) | |
if err != nil { | |
return nil, errors.Wrap(err, "failed to set subject key identifier") | |
} | |
certBytes, err := x509.CreateCertificate(rand.Reader, &certTmpl, caCert, key.Public(), caKey) | |
if err != nil { | |
return nil, errors.Wrap(err, "failed to create x509 certificate") | |
} | |
return x509.ParseCertificate(certBytes) | |
} | |
// SelfSignedCertificate creates a self signed certificate | |
func SelfSignedCertificate(cfg *CertCfg, key *rsa.PrivateKey) (*x509.Certificate, error) { | |
serial, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64)) | |
if err != nil { | |
return nil, err | |
} | |
cert := x509.Certificate{ | |
BasicConstraintsValid: true, | |
IsCA: cfg.IsCA, | |
KeyUsage: cfg.KeyUsages, | |
NotAfter: time.Now().Add(cfg.Validity), | |
NotBefore: time.Now().AddDate(0, 0, -2), | |
SerialNumber: serial, | |
Subject: cfg.Subject, | |
} | |
// verifies that the CN and/or OU for the cert is set | |
if len(cfg.Subject.CommonName) == 0 || len(cfg.Subject.OrganizationalUnit) == 0 { | |
return nil, errors.Errorf("certification's subject is not set, or invalid") | |
} | |
pub := key.Public() | |
cert.SubjectKeyId, err = generateSubjectKeyID(pub) | |
if err != nil { | |
return nil, errors.Wrap(err, "failed to set subject key identifier") | |
} | |
certBytes, err := x509.CreateCertificate(rand.Reader, &cert, &cert, key.Public(), key) | |
if err != nil { | |
return nil, errors.Wrap(err, "failed to create certificate") | |
} | |
return x509.ParseCertificate(certBytes) | |
} | |
// generateSubjectKeyID generates a SHA-1 hash of the subject public key. | |
func generateSubjectKeyID(pub crypto.PublicKey) ([]byte, error) { | |
var publicKeyBytes []byte | |
var err error | |
switch pub := pub.(type) { | |
case *rsa.PublicKey: | |
publicKeyBytes, err = asn1.Marshal(rsa.PublicKey{N: pub.N, E: pub.E}) | |
if err != nil { | |
return nil, errors.Wrap(err, "failed to Marshal ans1 public key") | |
} | |
fmt.Println(publicKeyBytes) | |
case *ecdsa.PublicKey: | |
publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y) | |
default: | |
return nil, errors.New("only RSA and ECDSA public keys supported") | |
} | |
hash := sha1.Sum(publicKeyBytes) | |
return hash[:], nil | |
} | |
// PrivateKeyToPem converts an rsa.PrivateKey object to pem string | |
func PrivateKeyToPem(key *rsa.PrivateKey) []byte { | |
keyInBytes := x509.MarshalPKCS1PrivateKey(key) | |
keyinPem := pem.EncodeToMemory( | |
&pem.Block{ | |
Type: "RSA PRIVATE KEY", | |
Bytes: keyInBytes, | |
}, | |
) | |
return keyinPem | |
} | |
// CertToPem converts an x509.Certificate object to a pem string | |
func CertToPem(cert *x509.Certificate) []byte { | |
certInPem := pem.EncodeToMemory( | |
&pem.Block{ | |
Type: "CERTIFICATE", | |
Bytes: cert.Raw, | |
}, | |
) | |
return certInPem | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment