forked from hswaw/hscloud
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
This commit is contained in:
parent
bade46d45f
commit
02e1598eb3
4 changed files with 129 additions and 0 deletions
|
@ -5,6 +5,7 @@ go_library(
|
|||
name = "go_default_library",
|
||||
srcs = [
|
||||
"certs.go",
|
||||
"crdb.go",
|
||||
"hspki.go",
|
||||
"kubernetes.go",
|
||||
"main.go",
|
||||
|
|
107
cluster/prodvider/crdb.go
Normal file
107
cluster/prodvider/crdb.go
Normal file
|
@ -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
|
||||
}
|
|
@ -16,6 +16,7 @@ message AuthenticateResponse {
|
|||
Result result = 1;
|
||||
KubernetesKeys kubernetes_keys = 2;
|
||||
HSPKIKeys hspki_keys = 3;
|
||||
CockroachDBKeys crdb_keys = 4;
|
||||
}
|
||||
|
||||
message KubernetesKeys {
|
||||
|
@ -32,6 +33,17 @@ message HSPKIKeys {
|
|||
string principal = 4;
|
||||
}
|
||||
|
||||
message CockroachDBKeys {
|
||||
message Cluster {
|
||||
string name = 1;
|
||||
bytes ca = 2;
|
||||
bytes cert = 3;
|
||||
bytes key = 4;
|
||||
string username = 5;
|
||||
}
|
||||
repeated Cluster clusters = 2;
|
||||
}
|
||||
|
||||
service Prodvider {
|
||||
rpc Authenticate(AuthenticateRequest) returns (AuthenticateResponse);
|
||||
}
|
||||
|
|
|
@ -81,10 +81,19 @@ func (p *prodvider) Authenticate(ctx context.Context, req *pb.AuthenticateReques
|
|||
return nil, status.Error(codes.Unavailable, "could not generate hspki keys")
|
||||
}
|
||||
|
||||
crdbWaw1Keys, err := p.crdbCreds(ctx, username, "waw1")
|
||||
if err != nil {
|
||||
glog.Errorf("crdbCreds(%q): %v", username, err)
|
||||
return nil, status.Error(codes.Unavailable, "could not generate crdb keys")
|
||||
}
|
||||
|
||||
return &pb.AuthenticateResponse{
|
||||
Result: pb.AuthenticateResponse_RESULT_AUTHENTICATED,
|
||||
KubernetesKeys: kubernetesKeys,
|
||||
HspkiKeys: hspkiKeys,
|
||||
CrdbKeys: &pb.CockroachDBKeys{
|
||||
Clusters: []*pb.CockroachDBKeys_Cluster{crdbWaw1Keys},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue