prod{access,vider}: implement

Prodaccess/Prodvider allow issuing short-lived certificates for all SSO
users to access the kubernetes cluster.

Currently, all users get a personal-$username namespace in which they
have adminitrative rights. Otherwise, they get no access.

In addition, we define a static CRB to allow some admins access to
everything. In the future, this will be more granular.

We also update relevant documentation.

Change-Id: Ia18594eea8a9e5efbb3e9a25a04a28bbd6a42153
diff --git a/cluster/prodaccess/prodaccess.go b/cluster/prodaccess/prodaccess.go
new file mode 100644
index 0000000..e0e8ec2
--- /dev/null
+++ b/cluster/prodaccess/prodaccess.go
@@ -0,0 +1,114 @@
+package main
+
+import (
+	"context"
+	"crypto/x509"
+	"flag"
+	"fmt"
+	"os"
+	"os/user"
+	"syscall"
+
+	"github.com/golang/glog"
+	"golang.org/x/crypto/ssh/terminal"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/credentials"
+
+	"code.hackerspace.pl/cluster/certs"
+	pb "code.hackerspace.pl/hscloud/cluster/prodvider/proto"
+)
+
+var (
+	flagProdvider string
+	flagUsername  string
+	flagForce     bool
+)
+
+func init() {
+	flag.Set("logtostderr", "true")
+}
+
+func main() {
+	user, err := user.Current()
+	if err == nil {
+		flagUsername = user.Username
+	}
+
+	flag.StringVar(&flagProdvider, "prodvider", "prodvider.hswaw.net:443", "Prodvider endpoint")
+	flag.StringVar(&flagUsername, "username", flagUsername, "Username to authenticate with")
+	flag.BoolVar(&flagForce, "force", false, "Force retrieving certificates even if they already exist")
+	flag.Parse()
+
+	if flagUsername == "" {
+		glog.Exitf("Username could not be detected, please provide with -username flag")
+	}
+
+	cp := x509.NewCertPool()
+	if ok := cp.AppendCertsFromPEM(certs.Data["ca-kube.crt"]); !ok {
+		glog.Exitf("Could not load k8s CA")
+	}
+
+	creds := credentials.NewClientTLSFromCert(cp, "")
+	conn, err := grpc.Dial(flagProdvider, grpc.WithTransportCredentials(creds))
+	if err != nil {
+		glog.Exitf("Could not dial prodvider: %v", err)
+	}
+
+	prodvider := pb.NewProdviderClient(conn)
+	ctx := context.Background()
+
+	if !needKubernetesCreds() && !flagForce {
+		fmt.Printf("Kubernetes credentials exist. Use `prodaccess -force` to force update.\n")
+		os.Exit(0)
+	}
+
+	attempts := 0
+	for {
+		ok := authenticate(ctx, prodvider)
+		attempts += 1
+		if !ok {
+			if attempts >= 3 {
+				os.Exit(1)
+			}
+		} else {
+			fmt.Printf("Good evening professor. I see you have driven here in your Ferrari.\n")
+			os.Exit(0)
+		}
+	}
+}
+
+func authenticate(ctx context.Context, prodvider pb.ProdviderClient) bool {
+	req := &pb.AuthenticateRequest{
+		Username: flagUsername,
+		Password: password(),
+	}
+
+	res, err := prodvider.Authenticate(ctx, req)
+	if err != nil {
+		glog.Exitf("Prodvider error: %v", err)
+	}
+
+	switch res.Result {
+	case pb.AuthenticateResponse_RESULT_AUTHENTICATED:
+		break
+	case pb.AuthenticateResponse_RESULT_INVALID_CREDENTIALS:
+		fmt.Printf("Invalid username or password.\n")
+		return false
+	default:
+		glog.Exitf("Unknown authentication result: %v", res.Result)
+	}
+
+	useKubernetesKeys(res.KubernetesKeys)
+
+	return true
+}
+
+func password() string {
+	fmt.Printf("Enter SSO/LDAP password for %s@hackerspace.pl: ", flagUsername)
+	bytePassword, err := terminal.ReadPassword(int(syscall.Stdin))
+	if err != nil {
+		return ""
+	}
+	fmt.Printf("\n")
+	return string(bytePassword)
+}