blob: fabf84dba6391df5bc4198946879d0ec650584e0 [file] [log] [blame]
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"
)
// hspkiSigner returns a cfssl signer (CA) for HSPKI, by loading the CA
// cert/key from Kubernetes.
func (p *prodvider) hspkiSigner(ctx context.Context) (*local.Signer, error) {
policy := &config.Signing{
Profiles: map[string]*config.SigningProfile{
"client-server": &config.SigningProfile{
Usage: []string{"signing", "key encipherment", "server auth", "client auth"},
ExpiryString: "30d",
},
},
Default: config.DefaultConfig(),
}
secret, err := p.k8s.CoreV1().Secrets("cert-manager").Get(ctx, "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)
}
// hspkiCreds returns a HSPKI certificate/key for an SSO user. The returned
// certificate is valida for both server and client usage.
func (p *prodvider) hspkiCreds(ctx context.Context, username string) (*pb.HSPKIKeys, error) {
principal := fmt.Sprintf("%s.sso.hswaw.net", username)
s, err := p.hspkiSigner(ctx)
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-server",
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
}