Created
April 21, 2021 17:14
-
-
Save mfojtik/375a302682679bac15fe8673f3afa56b to your computer and use it in GitHub Desktop.
This file contains hidden or 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 limitedclient | |
import ( | |
"context" | |
"crypto/rand" | |
"crypto/rsa" | |
"crypto/x509" | |
"encoding/pem" | |
"time" | |
certv1 "k8s.io/api/certificates/v1" | |
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | |
"k8s.io/client-go/kubernetes" | |
certv1client "k8s.io/client-go/kubernetes/typed/certificates/v1" | |
"k8s.io/client-go/rest" | |
) | |
type Factory struct { | |
lifetimeDuration time.Duration | |
originalRESTConfig *rest.Config | |
csrClient certv1client.CertificateSigningRequestInterface | |
} | |
func New(config *rest.Config) *Factory { | |
kubeClient := kubernetes.NewForConfigOrDie(config) | |
return &Factory{ | |
lifetimeDuration: 0, | |
originalRESTConfig: config, | |
csrClient: kubeClient.CertificatesV1().CertificateSigningRequests(), | |
} | |
} | |
func (f *Factory) WithLifetimeDuration(t time.Duration) *Factory { | |
f.lifetimeDuration = t | |
return f | |
} | |
func (f *Factory) Kubernetes(ctx context.Context) (kubernetes.Interface, error) { | |
key, cert, err := getClientKeys(ctx, f.csrClient) | |
if err != nil { | |
return nil, err | |
} | |
restConfig := *f.originalRESTConfig | |
restConfig.TLSClientConfig = rest.TLSClientConfig{ | |
ServerName: f.originalRESTConfig.ServerName, | |
CAData: f.originalRESTConfig.CAData, | |
CertData: cert, | |
KeyData: key, | |
} | |
return kubernetes.NewForConfig(&restConfig) | |
} | |
func getClientKeys(ctx context.Context, csrClient certv1client.CertificateSigningRequestInterface) ([]byte, []byte, error) { | |
key, err := getPrivateKey() | |
if err != nil { | |
return nil, nil, err | |
} | |
keyBytes := x509.MarshalPKCS1PrivateKey(key) | |
requestBytes, err := getCertificateRequestForKey(key) | |
if err != nil { | |
return nil, nil, err | |
} | |
csr, err := csrClient.Create(ctx, &certv1.CertificateSigningRequest{ | |
Spec: certv1.CertificateSigningRequestSpec{ | |
Request: requestBytes, | |
SignerName: "kubernetes.io/kube-apiserver-client", | |
Usages: []certv1.KeyUsage{certv1.UsageAny}, | |
}, | |
}, metav1.CreateOptions{}) | |
if err != nil { | |
return nil, nil, err | |
} | |
approvedCsr, err := csrClient.UpdateApproval(ctx, csr.Name, csr, metav1.UpdateOptions{}) | |
if err != nil { | |
return nil, nil, err | |
} | |
return keyBytes, approvedCsr.Status.Certificate, nil | |
} | |
func getCertificateRequestForKey(key *rsa.PrivateKey) ([]byte, error) { | |
request := x509.CertificateRequest{ | |
SignatureAlgorithm: x509.SHA256WithRSA, | |
PublicKey: key, | |
} | |
csr, err := x509.CreateCertificateRequest(rand.Reader, &request, key) | |
if err != nil { | |
return nil, err | |
} | |
csrPemBlock := &pem.Block{ | |
Type: "CERTIFICATE REQUEST", | |
Bytes: csr, | |
} | |
return pem.EncodeToMemory(csrPemBlock), nil | |
} | |
func getPrivateKey() (*rsa.PrivateKey, error) { | |
key, err := rsa.GenerateKey(rand.Reader, 2048) | |
if err != nil { | |
return nil, err | |
} | |
return key, nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment