Skip to content

Instantly share code, notes, and snippets.

@mjudeikis
Last active May 5, 2020 15:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mjudeikis/4c0fc47552897bf13e82414b7d8a9f28 to your computer and use it in GitHub Desktop.
Save mjudeikis/4c0fc47552897bf13e82414b7d8a9f28 to your computer and use it in GitHub Desktop.
APIServer certs bug
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