mirror of
https://gerrit.hackerspace.pl/hscloud
synced 2025-01-24 17:33:54 +00:00
radex
20288e8aa9
- remove defaults for -kubernetes_host and -prodvider_cn - pass cluster fqdn to KubernetesKeys.Cluster - hardcode prodvider.hswaw.net as one of prodvider cert's DNSNames to allow graceful transition to prodvider.k0.hswaw.net - add optional -crdb_cluster flag BONUS CONTENT: - use consistent credential duration for all certs + allow configuration via -credential_duration - fixes broken prodviding if username isn't all lowercase Change-Id: Ia801a16d7245d746e72f199a0900100ffc614dcf Reviewed-on: https://gerrit.hackerspace.pl/c/hscloud/+/2100 Reviewed-by: q3k <q3k@hackerspace.pl>
189 lines
4.9 KiB
Go
189 lines
4.9 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/golang/glog"
|
|
corev1 "k8s.io/api/core/v1"
|
|
rbacv1 "k8s.io/api/rbac/v1"
|
|
"k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/client-go/kubernetes"
|
|
"k8s.io/client-go/rest"
|
|
|
|
pb "code.hackerspace.pl/hscloud/cluster/prodvider/proto"
|
|
)
|
|
|
|
func (p *prodvider) kubernetesCreds(username string) (*pb.KubernetesKeys, error) {
|
|
o := fmt.Sprintf("sso:%s", username)
|
|
email := username + "@hackerspace.pl"
|
|
|
|
keyRaw, certBytes, err := p.makeKubernetesCertificate(email, o, time.Now().Add(flagCredentialDuration))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Build certificate chain from new cert and intermediate CA.
|
|
chainPEM := append(serializeCert(certBytes), serializeCert(p.intermediateCACert.Raw)...)
|
|
|
|
glog.Infof("Generated k8s certificate for %q", username)
|
|
return &pb.KubernetesKeys{
|
|
Cluster: flagKubeFqdn,
|
|
// APIServerCA
|
|
Ca: serializeCert(p.kubeCACert.Raw),
|
|
// Chain of new cert + intermediate CA
|
|
Cert: chainPEM,
|
|
Key: serializeKey(keyRaw),
|
|
}, nil
|
|
}
|
|
|
|
func (p *prodvider) kubernetesConnect() error {
|
|
keyRaw, certBytes, err := p.makeKubernetesCertificate("prodvider", "system:masters", time.Now().Add(30*24*time.Hour))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
glog.Infof("Generated k8s certificate for self (system:masters)")
|
|
|
|
// Build certificate chain from our cert and intermediate CA.
|
|
chainPEM := append(serializeCert(certBytes), serializeCert(p.intermediateCACert.Raw)...)
|
|
|
|
config := &rest.Config{
|
|
Host: flagKubeApiserverAddress,
|
|
TLSClientConfig: rest.TLSClientConfig{
|
|
// Chain to authenticate ourselves (us + intermediate CA).
|
|
CertData: chainPEM,
|
|
KeyData: serializeKey(keyRaw),
|
|
// APIServer CA for verification.
|
|
CAData: serializeCert(p.kubeCACert.Raw),
|
|
},
|
|
}
|
|
|
|
cs, err := kubernetes.NewForConfig(config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
p.k8s = cs
|
|
|
|
return nil
|
|
}
|
|
|
|
// kubernetesSetupUser ensures that for a given SSO username we:
|
|
// - have a personal-<username> namespace
|
|
// - have a sso:<username>:personal rolebinding that binds
|
|
// system:admin-namespace to the user within their personal namespace
|
|
// - have a sso:<username>:global clusterrolebinding that binds
|
|
// system:viewer to the user at cluster level
|
|
func (p *prodvider) kubernetesSetupUser(ctx context.Context, username string) error {
|
|
namespace := "personal-" + username
|
|
if err := p.ensureNamespace(ctx, namespace); err != nil {
|
|
return err
|
|
}
|
|
if err := p.ensureRoleBindingPersonal(ctx, namespace, username); err != nil {
|
|
return err
|
|
}
|
|
if err := p.ensureClusterRoleBindingGlobal(ctx, username); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *prodvider) ensureNamespace(ctx context.Context, name string) error {
|
|
_, err := p.k8s.CoreV1().Namespaces().Get(ctx, name, metav1.GetOptions{})
|
|
switch {
|
|
case err == nil:
|
|
// Already exists, nothing to do
|
|
return nil
|
|
case errors.IsNotFound(err):
|
|
break
|
|
default:
|
|
// Something went wrong.
|
|
return err
|
|
}
|
|
ns := &corev1.Namespace{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: name,
|
|
},
|
|
}
|
|
_, err = p.k8s.CoreV1().Namespaces().Create(ctx, ns, metav1.CreateOptions{})
|
|
return err
|
|
}
|
|
|
|
func (p *prodvider) ensureRoleBindingPersonal(ctx context.Context, namespace, username string) error {
|
|
name := "sso:" + username + ":personal"
|
|
rb := &rbacv1.RoleBinding{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: name,
|
|
Namespace: namespace,
|
|
},
|
|
Subjects: []rbacv1.Subject{
|
|
{
|
|
APIGroup: "rbac.authorization.k8s.io",
|
|
Kind: "User",
|
|
Name: username + "@hackerspace.pl",
|
|
},
|
|
},
|
|
RoleRef: rbacv1.RoleRef{
|
|
APIGroup: "rbac.authorization.k8s.io",
|
|
Kind: "ClusterRole",
|
|
Name: "system:admin-namespace",
|
|
},
|
|
}
|
|
|
|
rbs := p.k8s.RbacV1().RoleBindings(namespace)
|
|
_, err := rbs.Get(ctx, name, metav1.GetOptions{})
|
|
switch {
|
|
case err == nil:
|
|
// Already exists, update.
|
|
_, err = rbs.Update(ctx, rb, metav1.UpdateOptions{})
|
|
return err
|
|
case errors.IsNotFound(err):
|
|
// Create.
|
|
_, err = rbs.Create(ctx, rb, metav1.CreateOptions{})
|
|
return err
|
|
default:
|
|
// Something went wrong.
|
|
return err
|
|
}
|
|
}
|
|
|
|
func (p *prodvider) ensureClusterRoleBindingGlobal(ctx context.Context, username string) error {
|
|
name := "sso:" + username + ":global"
|
|
rb := &rbacv1.ClusterRoleBinding{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: name,
|
|
},
|
|
Subjects: []rbacv1.Subject{
|
|
{
|
|
APIGroup: "rbac.authorization.k8s.io",
|
|
Kind: "User",
|
|
Name: username + "@hackerspace.pl",
|
|
},
|
|
},
|
|
RoleRef: rbacv1.RoleRef{
|
|
APIGroup: "rbac.authorization.k8s.io",
|
|
Kind: "ClusterRole",
|
|
Name: "system:viewer",
|
|
},
|
|
}
|
|
|
|
crbs := p.k8s.RbacV1().ClusterRoleBindings()
|
|
_, err := crbs.Get(ctx, name, metav1.GetOptions{})
|
|
switch {
|
|
case err == nil:
|
|
// Already exists, update.
|
|
_, err = crbs.Update(ctx, rb, metav1.UpdateOptions{})
|
|
return err
|
|
case errors.IsNotFound(err):
|
|
// Create.
|
|
_, err = crbs.Create(ctx, rb, metav1.CreateOptions{})
|
|
return err
|
|
default:
|
|
// Something went wrong.
|
|
return err
|
|
}
|
|
}
|