| 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.KeyRequest{ |
| 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 |
| } |