*: developer machine HSPKI credentials

In addition to k8s certificates, prodaccess now issues HSPKI
certificates, with DN=$username.sso.hswaw.net. These are installed into
XDG_CONFIG_HOME (or os equiv).

//go/pki will now automatically attempt to load these certificates. This
means you can now run any pki-dependant tool with -hspki_disable, and
with automatic mTLS!

Change-Id: I5b28e193e7c968d621bab0d42aabd6f0510fed6d
diff --git a/cluster/prodvider/hspki.go b/cluster/prodvider/hspki.go
new file mode 100644
index 0000000..243a424
--- /dev/null
+++ b/cluster/prodvider/hspki.go
@@ -0,0 +1,99 @@
+package main
+
+import (
+	"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"
+)
+
+func (p *prodvider) hspkiSigner() (*local.Signer, error) {
+	policy := &config.Signing{
+		Profiles: map[string]*config.SigningProfile{
+			"client": &config.SigningProfile{
+				Usage:        []string{"signing", "key encipherment", "client auth"},
+				ExpiryString: "30d",
+			},
+		},
+		Default: config.DefaultConfig(),
+	}
+
+	secret, err := p.k8s.CoreV1().Secrets("cert-manager").Get("pki-selfsigned-cert", 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)
+}
+
+func (p *prodvider) hspkiCreds(username string) (*pb.HSPKIKeys, error) {
+	principal := fmt.Sprintf("%s.sso.hswaw.net", username)
+
+	s, err := p.hspkiSigner()
+	if err != nil {
+		return nil, fmt.Errorf("hspkiSigner: %w", err)
+	}
+
+	signerCert, _ := s.Certificate("", "")
+	req := &csr.CertificateRequest{
+		CN: principal,
+		KeyRequest: &csr.BasicKeyRequest{
+			A: "rsa",
+			S: 4096,
+		},
+		Names: []csr.Name{
+			{
+				O:  "prodvider",
+				OU: fmt.Sprintf("Prodvider HSPKI Cert for %s", 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{},
+		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.HSPKIKeys{
+		Ca:        caPEM,
+		Cert:      certPEM,
+		Key:       keyPEM,
+		Principal: principal,
+	}, nil
+}