forked from hswaw/hscloud
Serge Bazanski
3a6d67e0c4
This gets rid of cfssl for the kubernetes bits of prodvider, instead using plain crypto/x509. This also allows to support our new fancy ED25519 CA. Change-Id: If677b3f4523014f56ea802b87499d1c0eb6d92e9 Reviewed-on: https://gerrit.hackerspace.pl/c/hscloud/+/1489 Reviewed-by: q3k <q3k@hackerspace.pl>
172 lines
4.5 KiB
Go
172 lines
4.5 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/ed25519"
|
|
"crypto/x509"
|
|
"encoding/pem"
|
|
"flag"
|
|
"fmt"
|
|
"math/rand"
|
|
"net"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/golang/glog"
|
|
"google.golang.org/grpc"
|
|
"k8s.io/client-go/kubernetes"
|
|
|
|
pb "code.hackerspace.pl/hscloud/cluster/prodvider/proto"
|
|
)
|
|
|
|
var (
|
|
flagLDAPServer string
|
|
flagLDAPBindDN string
|
|
flagLDAPGroupSearchBase string
|
|
flagListenAddress string
|
|
flagKubernetesHost string
|
|
|
|
flagCACertificatePath string
|
|
flagCAKeyPath string
|
|
flagKubeCACertificatePath string
|
|
|
|
flagProdviderCN string
|
|
)
|
|
|
|
func init() {
|
|
flag.Set("logtostderr", "true")
|
|
}
|
|
|
|
type prodvider struct {
|
|
k8s *kubernetes.Clientset
|
|
srv *grpc.Server
|
|
|
|
intermediateCAKey ed25519.PrivateKey
|
|
intermediateCACert *x509.Certificate
|
|
kubeCACert *x509.Certificate
|
|
}
|
|
|
|
func loadCert(path string) (*x509.Certificate, error) {
|
|
b, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
block, _ := pem.Decode(b)
|
|
if block == nil {
|
|
return nil, fmt.Errorf("no PEM block found")
|
|
}
|
|
if block.Type != "CERTIFICATE" {
|
|
return nil, fmt.Errorf("unexpected PEM block: %q", block.Type)
|
|
}
|
|
return x509.ParseCertificate(block.Bytes)
|
|
}
|
|
|
|
func loadKey(path string) (ed25519.PrivateKey, error) {
|
|
bytes, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
block, _ := pem.Decode(bytes)
|
|
if block == nil {
|
|
return nil, fmt.Errorf("no PEM block found")
|
|
}
|
|
if block.Type != "PRIVATE KEY" {
|
|
return nil, fmt.Errorf("unexpected PEM block: %q", block.Type)
|
|
}
|
|
key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if k, ok := key.(ed25519.PrivateKey); ok {
|
|
return k, nil
|
|
}
|
|
return nil, fmt.Errorf("not an ED25519 key")
|
|
}
|
|
|
|
func newProdvider() *prodvider {
|
|
kubeCACert, err := loadCert(flagKubeCACertificatePath)
|
|
if err != nil {
|
|
glog.Exitf("Loading kube CA certificate failed: %v", err)
|
|
}
|
|
intermediateCACert, err := loadCert(flagCACertificatePath)
|
|
if err != nil {
|
|
glog.Exitf("Loading intermediate CAcertificate failed: %v", err)
|
|
}
|
|
intermediateCAKey, err := loadKey(flagCAKeyPath)
|
|
return &prodvider{
|
|
intermediateCAKey: intermediateCAKey,
|
|
intermediateCACert: intermediateCACert,
|
|
kubeCACert: kubeCACert,
|
|
}
|
|
}
|
|
|
|
// Timebomb restarts the prodvider after a deadline, usually 7 days +/- 4 days.
|
|
// This is to ensure we serve with up-to-date certificates and that the service
|
|
// can still come up after restart.
|
|
func timebomb(srv *grpc.Server) {
|
|
deadline := time.Now()
|
|
deadline = deadline.Add(3 * 24 * time.Hour)
|
|
rand.Seed(time.Now().UnixNano())
|
|
jitter := rand.Intn(8 * 24 * 60 * 60)
|
|
deadline = deadline.Add(time.Duration(jitter) * time.Second)
|
|
|
|
glog.Infof("Timebomb deadline set to %v", deadline)
|
|
|
|
t := time.NewTicker(time.Minute)
|
|
for {
|
|
<-t.C
|
|
if time.Now().After(deadline) {
|
|
break
|
|
}
|
|
}
|
|
|
|
// Start killing connections, and wait one minute...
|
|
go srv.GracefulStop()
|
|
<-t.C
|
|
glog.Infof("Timebomb deadline exceeded, restarting.")
|
|
os.Exit(0)
|
|
}
|
|
|
|
func main() {
|
|
flag.StringVar(&flagLDAPServer, "ldap_server", "ldap.hackerspace.pl:636", "Address of LDAP server")
|
|
flag.StringVar(&flagLDAPBindDN, "ldap_bind_dn", "uid=%s,ou=People,dc=hackerspace,dc=pl", "LDAP Bind DN")
|
|
flag.StringVar(&flagLDAPGroupSearchBase, "ldap_group_search_base_dn", "ou=Group,dc=hackerspace,dc=pl", "LDAP Group Search Base DN")
|
|
flag.StringVar(&flagListenAddress, "listen_address", "127.0.0.1:8080", "gRPC listen address")
|
|
flag.StringVar(&flagKubernetesHost, "kubernetes_host", "k0.hswaw.net:4001", "Kubernetes API host")
|
|
|
|
flag.StringVar(&flagCACertificatePath, "ca_certificate_path", "", "CA certificate path (for signer)")
|
|
flag.StringVar(&flagCAKeyPath, "ca_key_path", "", "CA key path (for signer)")
|
|
flag.StringVar(&flagKubeCACertificatePath, "kube_ca_certificate_path", "", "CA certificate path (for checking kube apiserver)")
|
|
|
|
flag.StringVar(&flagProdviderCN, "prodvider_cn", "prodvider.hswaw.net", "CN of certificate that prodvider will use")
|
|
flag.Parse()
|
|
|
|
if flagCACertificatePath == "" || flagCAKeyPath == "" {
|
|
glog.Exitf("CA certificate and key must be provided")
|
|
}
|
|
|
|
p := newProdvider()
|
|
err := p.kubernetesConnect()
|
|
if err != nil {
|
|
glog.Exitf("Could not connect to kubernetes: %v", err)
|
|
}
|
|
creds := p.selfCreds()
|
|
|
|
// Start serving gRPC
|
|
grpcLis, err := net.Listen("tcp", flagListenAddress)
|
|
if err != nil {
|
|
glog.Exitf("Could not listen for gRPC on %q: %v", flagListenAddress, err)
|
|
}
|
|
|
|
glog.Infof("Starting gRPC on %q...", flagListenAddress)
|
|
grpcSrv := grpc.NewServer(creds)
|
|
|
|
pb.RegisterProdviderServer(grpcSrv, p)
|
|
|
|
go timebomb(grpcSrv)
|
|
|
|
err = grpcSrv.Serve(grpcLis)
|
|
if err != nil {
|
|
glog.Exitf("Could not serve gRPC: %v", err)
|
|
}
|
|
}
|