4
0
Fork 2
mirror of https://gerrit.hackerspace.pl/hscloud synced 2025-02-10 23:06:45 +00:00

cluster/prodaccess: add multi-cluster support

- allow passing -cluster to select another cluster (k0 remains the default for the time being)
- default prodvider dns to prodvider.<cluster>.hswaw.net
- scope kube config username and certs storage by cluster name

additionally:

- force username to be lowercase (LDAP is case-insensitive, but e.g. kubernetes namespaces are not)
- fix some Go deprecations

Change-Id: Ibf4a6ced7a635940f6a7c568c79714cd8ac60ce9
Reviewed-on: https://gerrit.hackerspace.pl/c/hscloud/+/2101
Reviewed-by: radex <radex@hackerspace.pl>
This commit is contained in:
radex 2025-01-11 00:58:37 +01:00
parent 0565af617a
commit 63f0adde2f
4 changed files with 44 additions and 25 deletions

View file

@ -12,12 +12,13 @@ go_library(
deps = [
"//cluster/certs",
"//cluster/prodvider/proto",
"//cluster/clustercfg/clusters",
"//go/pki",
"//go/workspace",
"@com_github_golang_glog//:glog",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_grpc//credentials",
"@org_golang_x_crypto//ssh/terminal",
"@org_golang_x_term//:term",
],
)

View file

@ -1,7 +1,6 @@
package main
import (
"io/ioutil"
"os"
"github.com/golang/glog"
@ -28,7 +27,7 @@ func useHSPKIKeys(keys *pb.HSPKIKeys) {
{path + "/tls.crt", keys.Cert},
{path + "/tls.key", keys.Key},
} {
err := ioutil.WriteFile(el.target, el.data, 400)
err := os.WriteFile(el.target, el.data, 400)
if err != nil {
glog.Exitf("Failed to write %q: %v", el.target, err)
}

View file

@ -4,7 +4,6 @@ import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
@ -13,25 +12,28 @@ import (
"github.com/golang/glog"
"code.hackerspace.pl/hscloud/cluster/clustercfg/clusters"
pb "code.hackerspace.pl/hscloud/cluster/prodvider/proto"
"code.hackerspace.pl/hscloud/go/workspace"
)
func kubernetesPaths() (string, string, string) {
func kubernetesPaths(cluster clusters.Cluster) (string, string, string) {
ws, err := workspace.Get()
if err != nil {
glog.Exitf("%v", err)
}
localKey := path.Join(ws, ".kubectl", fmt.Sprintf("%s.key", flagUsername))
localCert := path.Join(ws, ".kubectl", fmt.Sprintf("%s.crt", flagUsername))
localCA := path.Join(ws, ".kubectl", fmt.Sprintf("ca.crt"))
root := path.Join(ws, ".kubectl", cluster.Name)
localKey := path.Join(root, fmt.Sprintf("%s.key", flagUsername))
localCert := path.Join(root, fmt.Sprintf("%s.crt", flagUsername))
localCA := path.Join(root, fmt.Sprintf("ca.crt"))
return localKey, localCert, localCA
}
func needKubernetesCreds() bool {
localKey, localCert, _ := kubernetesPaths()
func needKubernetesCreds(cluster clusters.Cluster) bool {
localKey, localCert, _ := kubernetesPaths(cluster)
// Check for existence of cert/key.
if _, err := os.Stat(localKey); os.IsNotExist(err) {
@ -64,21 +66,21 @@ func needKubernetesCreds() bool {
return false
}
func useKubernetesKeys(keys *pb.KubernetesKeys) {
localKey, localCert, localCA := kubernetesPaths()
func useKubernetesKeys(keys *pb.KubernetesKeys, cluster clusters.Cluster) {
localKey, localCert, localCA := kubernetesPaths(cluster)
parent := filepath.Dir(localKey)
if _, err := os.Stat(parent); os.IsNotExist(err) {
os.MkdirAll(parent, 0700)
}
if err := ioutil.WriteFile(localKey, keys.Key, 0600); err != nil {
if err := os.WriteFile(localKey, keys.Key, 0600); err != nil {
glog.Exitf("WriteFile(%q): %v", localKey, err)
}
if err := ioutil.WriteFile(localCert, keys.Cert, 0600); err != nil {
if err := os.WriteFile(localCert, keys.Cert, 0600); err != nil {
glog.Exitf("WriteFile(%q): %v", localCert, err)
}
if err := ioutil.WriteFile(localCA, keys.Ca, 0600); err != nil {
if err := os.WriteFile(localCA, keys.Ca, 0600); err != nil {
glog.Exitf("WriteFile(%q): %v", localCA, err)
}
@ -96,8 +98,10 @@ func useKubernetesKeys(keys *pb.KubernetesKeys) {
"--embed-certs=true",
"--server=https://"+keys.Cluster+":4001")
clusterUsername := fmt.Sprintf("%s@%s", flagUsername, keys.Cluster)
kubectl("config",
"set-credentials", flagUsername,
"set-credentials", clusterUsername,
"--client-certificate="+localCert,
"--client-key="+localKey,
"--embed-certs=true")
@ -105,7 +109,7 @@ func useKubernetesKeys(keys *pb.KubernetesKeys) {
kubectl("config",
"set-context", keys.Cluster,
"--cluster="+keys.Cluster,
"--user="+flagUsername)
"--user="+clusterUsername)
kubectl("config", "use-context", keys.Cluster)
}

View file

@ -4,22 +4,25 @@ import (
"context"
"crypto/x509"
"flag"
"strings"
"fmt"
"log"
"os"
"os/user"
"strings"
"syscall"
"github.com/golang/glog"
"golang.org/x/crypto/ssh/terminal"
"golang.org/x/term"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"code.hackerspace.pl/hscloud/cluster/certs"
"code.hackerspace.pl/hscloud/cluster/clustercfg/clusters"
pb "code.hackerspace.pl/hscloud/cluster/prodvider/proto"
)
var (
flagCluster string
flagProdvider string
flagUsername string
flagForce bool
@ -35,16 +38,28 @@ func main() {
flagUsername = user.Username
}
flag.StringVar(&flagProdvider, "prodvider", "prodvider.hswaw.net:443", "Prodvider endpoint")
flag.StringVar(&flagCluster, "cluster", "k0", "Cluster short name")
flag.StringVar(&flagProdvider, "prodvider", "", "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()
flagUsername = strings.ToLower(flagUsername)
if flagUsername == "" {
glog.Exitf("Username could not be detected, please provide with -username flag")
}
cluster, ok := clusters.Clusters[flagCluster]
if !ok {
log.Fatalf("Unknown cluster: %q", flagCluster)
}
if flagProdvider == "" {
flagProdvider = "prodvider." + cluster.Name + ".hswaw.net:443"
}
cp := x509.NewCertPool()
// TODO(radex): vary certs based on cluster
if ok := cp.AppendCertsFromPEM(certs.Data["ca-kube.crt"]); !ok {
glog.Exitf("Could not load k8s CA")
}
@ -58,14 +73,14 @@ func main() {
prodvider := pb.NewProdviderClient(conn)
ctx := context.Background()
if !needKubernetesCreds() && !flagForce {
if !needKubernetesCreds(cluster) && !flagForce {
fmt.Printf("Kubernetes credentials exist. Use `prodaccess -force` to force update.\n")
os.Exit(0)
}
attempts := 0
for {
ok := authenticate(ctx, prodvider)
ok := authenticate(ctx, prodvider, cluster)
attempts += 1
if !ok {
if attempts >= 3 {
@ -78,7 +93,7 @@ func main() {
}
}
func authenticate(ctx context.Context, prodvider pb.ProdviderClient) bool {
func authenticate(ctx context.Context, prodvider pb.ProdviderClient, cluster clusters.Cluster) bool {
req := &pb.AuthenticateRequest{
Username: flagUsername,
Password: password(),
@ -99,7 +114,7 @@ func authenticate(ctx context.Context, prodvider pb.ProdviderClient) bool {
glog.Exitf("Unknown authentication result: %v", res.Result)
}
useKubernetesKeys(res.KubernetesKeys)
useKubernetesKeys(res.KubernetesKeys, cluster)
fmt.Printf("-> Kubernetes credentials installed\n")
useHSPKIKeys(res.HspkiKeys)
fmt.Printf("-> HSPKI credentials installed\n")
@ -109,7 +124,7 @@ func authenticate(ctx context.Context, prodvider pb.ProdviderClient) bool {
func password() string {
fmt.Printf("Enter SSO/LDAP password for %s@hackerspace.pl: ", flagUsername)
bytePassword, err := terminal.ReadPassword(int(syscall.Stdin))
bytePassword, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
return ""
}