cluster/prodvider: emit crdb certs
This emits short-lived user credentials for a `dev-user` in crdb-waw1
any time someone prodaccesses.
Change-Id: I0266a05c1f02225d762cfd2ca61976af0658639d
diff --git a/cluster/prodvider/crdb.go b/cluster/prodvider/crdb.go
new file mode 100644
index 0000000..348754c
--- /dev/null
+++ b/cluster/prodvider/crdb.go
@@ -0,0 +1,107 @@
+package main
+
+import (
+ "context"
+ "encoding/pem"
+ "fmt"
+ "time"
+
+ "github.com/cloudflare/cfssl/config"
+ "github.com/cloudflare/cfssl/csr"
+ "github.com/cloudflare/cfssl/helpers"
+ "github.com/cloudflare/cfssl/signer"
+ "github.com/cloudflare/cfssl/signer/local"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+ pb "code.hackerspace.pl/hscloud/cluster/prodvider/proto"
+)
+
+// crdbSigner returns a cfssl signer (CA) for a crdb cluster, by loading the CA
+// cert/key from Kubernetes.
+func (p *prodvider) crdbSigner(ctx context.Context, cluster string) (*local.Signer, error) {
+ policy := &config.Signing{
+ Profiles: map[string]*config.SigningProfile{
+ "client": &config.SigningProfile{
+ Usage: []string{"signing", "key encipherment"},
+ ExpiryString: "30d",
+ },
+ },
+ Default: config.DefaultConfig(),
+ }
+
+ namespace := fmt.Sprintf("crdb-%s", cluster)
+
+ secret, err := p.k8s.CoreV1().Secrets(namespace).Get(ctx, "cluster-ca", metav1.GetOptions{})
+ if err != nil {
+ return nil, fmt.Errorf("hspki secret get failed: %w", err)
+ }
+
+ parsedCa, err := helpers.ParseCertificatePEM(secret.Data["tls.crt"])
+ if err != nil {
+ return nil, fmt.Errorf("when parsing tls.crt: %w", err)
+ }
+
+ priv, err := helpers.ParsePrivateKeyPEMWithPassword(secret.Data["tls.key"], nil)
+ if err != nil {
+ return nil, fmt.Errorf("when parsing tls.key: %w", err)
+ }
+
+ return local.NewSigner(priv, parsedCa, signer.DefaultSigAlgo(priv), policy)
+}
+
+// crdbCreds returns a crdb certificate/key for an SSO useron a given cluster.
+// The returned certificate is valid for connecting to crdb.
+func (p *prodvider) crdbCreds(ctx context.Context, username, cluster string) (*pb.CockroachDBKeys_Cluster, error) {
+ username = fmt.Sprintf("dev-%s", username)
+
+ s, err := p.crdbSigner(ctx, cluster)
+ if err != nil {
+ return nil, fmt.Errorf("hspkiSigner: %w", err)
+ }
+
+ signerCert, _ := s.Certificate("", "")
+ req := &csr.CertificateRequest{
+ CN: username,
+ KeyRequest: &csr.BasicKeyRequest{
+ A: "rsa",
+ S: 4096,
+ },
+ Names: []csr.Name{
+ {
+ O: "prodvider",
+ },
+ },
+ Hosts: []string{username},
+ }
+
+ g := &csr.Generator{
+ Validator: func(req *csr.CertificateRequest) error { return nil },
+ }
+
+ csrPEM, keyPEM, err := g.ProcessRequest(req)
+ if err != nil {
+ return nil, fmt.Errorf("when making CSR: %w", err)
+ }
+
+ signReq := signer.SignRequest{
+ Hosts: []string{username},
+ Request: string(csrPEM),
+ Profile: "client",
+ NotAfter: time.Now().Add(9 * time.Hour),
+ }
+
+ certPEM, err := s.Sign(signReq)
+ if err != nil {
+ return nil, fmt.Errorf("when issuing certificate: %w", err)
+ }
+
+ caPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: signerCert.Raw})
+
+ return &pb.CockroachDBKeys_Cluster{
+ Name: cluster,
+ Ca: caPEM,
+ Cert: certPEM,
+ Key: keyPEM,
+ Username: username,
+ }, nil
+}