Serge Bazanski | f3312ef | 2020-08-01 17:15:52 +0200 | [diff] [blame] | 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "encoding/pem" |
| 5 | "fmt" |
| 6 | "time" |
| 7 | |
| 8 | "github.com/cloudflare/cfssl/config" |
| 9 | "github.com/cloudflare/cfssl/csr" |
| 10 | "github.com/cloudflare/cfssl/helpers" |
| 11 | "github.com/cloudflare/cfssl/signer" |
| 12 | "github.com/cloudflare/cfssl/signer/local" |
| 13 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| 14 | |
| 15 | pb "code.hackerspace.pl/hscloud/cluster/prodvider/proto" |
| 16 | ) |
| 17 | |
Serge Bazanski | 4ded56a | 2020-08-01 22:01:05 +0200 | [diff] [blame] | 18 | // hspkiSigner returns a cfssl signer (CA) for HSPKI, by loading the CA |
| 19 | // cert/key from Kubernetes. |
Serge Bazanski | f3312ef | 2020-08-01 17:15:52 +0200 | [diff] [blame] | 20 | func (p *prodvider) hspkiSigner() (*local.Signer, error) { |
| 21 | policy := &config.Signing{ |
| 22 | Profiles: map[string]*config.SigningProfile{ |
Serge Bazanski | 4ded56a | 2020-08-01 22:01:05 +0200 | [diff] [blame] | 23 | "client-server": &config.SigningProfile{ |
| 24 | Usage: []string{"signing", "key encipherment", "server auth", "client auth"}, |
Serge Bazanski | f3312ef | 2020-08-01 17:15:52 +0200 | [diff] [blame] | 25 | ExpiryString: "30d", |
| 26 | }, |
| 27 | }, |
| 28 | Default: config.DefaultConfig(), |
| 29 | } |
| 30 | |
| 31 | secret, err := p.k8s.CoreV1().Secrets("cert-manager").Get("pki-selfsigned-cert", metav1.GetOptions{}) |
| 32 | if err != nil { |
| 33 | return nil, fmt.Errorf("hspki secret get failed: %w", err) |
| 34 | } |
| 35 | |
| 36 | parsedCa, err := helpers.ParseCertificatePEM(secret.Data["tls.crt"]) |
| 37 | if err != nil { |
| 38 | return nil, fmt.Errorf("when parsing tls.crt: %w", err) |
| 39 | } |
| 40 | |
| 41 | priv, err := helpers.ParsePrivateKeyPEMWithPassword(secret.Data["tls.key"], nil) |
| 42 | if err != nil { |
| 43 | return nil, fmt.Errorf("when parsing tls.key: %w", err) |
| 44 | } |
| 45 | |
| 46 | return local.NewSigner(priv, parsedCa, signer.DefaultSigAlgo(priv), policy) |
| 47 | } |
| 48 | |
Serge Bazanski | 4ded56a | 2020-08-01 22:01:05 +0200 | [diff] [blame] | 49 | // hspkiCreds returns a HSPKI certificate/key for an SSO user. The returned |
| 50 | // certificate is valida for both server and client usage. |
Serge Bazanski | f3312ef | 2020-08-01 17:15:52 +0200 | [diff] [blame] | 51 | func (p *prodvider) hspkiCreds(username string) (*pb.HSPKIKeys, error) { |
| 52 | principal := fmt.Sprintf("%s.sso.hswaw.net", username) |
| 53 | |
| 54 | s, err := p.hspkiSigner() |
| 55 | if err != nil { |
| 56 | return nil, fmt.Errorf("hspkiSigner: %w", err) |
| 57 | } |
| 58 | |
| 59 | signerCert, _ := s.Certificate("", "") |
| 60 | req := &csr.CertificateRequest{ |
| 61 | CN: principal, |
| 62 | KeyRequest: &csr.BasicKeyRequest{ |
| 63 | A: "rsa", |
| 64 | S: 4096, |
| 65 | }, |
| 66 | Names: []csr.Name{ |
| 67 | { |
| 68 | O: "prodvider", |
| 69 | OU: fmt.Sprintf("Prodvider HSPKI Cert for %s", username), |
| 70 | }, |
| 71 | }, |
| 72 | } |
| 73 | |
| 74 | g := &csr.Generator{ |
| 75 | Validator: func(req *csr.CertificateRequest) error { return nil }, |
| 76 | } |
| 77 | |
| 78 | csrPEM, keyPEM, err := g.ProcessRequest(req) |
| 79 | if err != nil { |
| 80 | return nil, fmt.Errorf("when making CSR: %w", err) |
| 81 | } |
| 82 | |
| 83 | signReq := signer.SignRequest{ |
| 84 | Hosts: []string{}, |
| 85 | Request: string(csrPEM), |
Serge Bazanski | 4ded56a | 2020-08-01 22:01:05 +0200 | [diff] [blame] | 86 | Profile: "client-server", |
Serge Bazanski | f3312ef | 2020-08-01 17:15:52 +0200 | [diff] [blame] | 87 | NotAfter: time.Now().Add(9 * time.Hour), |
| 88 | } |
| 89 | |
| 90 | certPEM, err := s.Sign(signReq) |
| 91 | if err != nil { |
| 92 | return nil, fmt.Errorf("when issuing certificate: %w", err) |
| 93 | } |
| 94 | |
| 95 | caPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: signerCert.Raw}) |
| 96 | |
| 97 | return &pb.HSPKIKeys{ |
| 98 | Ca: caPEM, |
| 99 | Cert: certPEM, |
| 100 | Key: keyPEM, |
| 101 | Principal: principal, |
| 102 | }, nil |
| 103 | } |