blob: 4f73ce470c99a95563c426035437818890d6cc74 [file] [log] [blame]
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +02001package main
2
3import (
Serge Bazanski0754ed82020-11-27 09:42:59 +00004 "context"
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +02005 "fmt"
6 "time"
7
8 "github.com/golang/glog"
9 corev1 "k8s.io/api/core/v1"
10 rbacv1 "k8s.io/api/rbac/v1"
11 "k8s.io/apimachinery/pkg/api/errors"
12 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13 "k8s.io/client-go/kubernetes"
14 "k8s.io/client-go/rest"
15
16 pb "code.hackerspace.pl/hscloud/cluster/prodvider/proto"
17)
18
19func (p *prodvider) kubernetesCreds(username string) (*pb.KubernetesKeys, error) {
20 o := fmt.Sprintf("sso:%s", username)
Serge Bazanski3a6d67e2023-03-31 22:36:27 +000021 email := username + "@hackerspace.pl"
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020022
Serge Bazanski3a6d67e2023-03-31 22:36:27 +000023 keyRaw, certBytes, err := p.makeKubernetesCertificate(email, o, time.Now().Add(13*time.Hour))
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020024 if err != nil {
25 return nil, err
26 }
27
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020028 // Build certificate chain from new cert and intermediate CA.
Serge Bazanski3a6d67e2023-03-31 22:36:27 +000029 chainPEM := append(serializeCert(certBytes), serializeCert(p.intermediateCACert.Raw)...)
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020030
31 glog.Infof("Generated k8s certificate for %q", username)
32 return &pb.KubernetesKeys{
33 Cluster: "k0.hswaw.net",
34 // APIServerCA
Serge Bazanski3a6d67e2023-03-31 22:36:27 +000035 Ca: serializeCert(p.kubeCACert.Raw),
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020036 // Chain of new cert + intermediate CA
37 Cert: chainPEM,
Serge Bazanski3a6d67e2023-03-31 22:36:27 +000038 Key: serializeKey(keyRaw),
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020039 }, nil
40}
41
42func (p *prodvider) kubernetesConnect() error {
Serge Bazanski3a6d67e2023-03-31 22:36:27 +000043 keyRaw, certBytes, err := p.makeKubernetesCertificate("prodvider", "system:masters", time.Now().Add(30*24*time.Hour))
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020044 if err != nil {
45 return err
46 }
47
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020048 glog.Infof("Generated k8s certificate for self (system:masters)")
49
50 // Build certificate chain from our cert and intermediate CA.
Serge Bazanski3a6d67e2023-03-31 22:36:27 +000051 chainPEM := append(serializeCert(certBytes), serializeCert(p.intermediateCACert.Raw)...)
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020052
53 config := &rest.Config{
54 Host: flagKubernetesHost,
55 TLSClientConfig: rest.TLSClientConfig{
56 // Chain to authenticate ourselves (us + intermediate CA).
57 CertData: chainPEM,
Serge Bazanski3a6d67e2023-03-31 22:36:27 +000058 KeyData: serializeKey(keyRaw),
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020059 // APIServer CA for verification.
Serge Bazanski3a6d67e2023-03-31 22:36:27 +000060 CAData: serializeCert(p.kubeCACert.Raw),
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020061 },
62 }
63
64 cs, err := kubernetes.NewForConfig(config)
65 if err != nil {
66 return err
67 }
68
69 p.k8s = cs
70
71 return nil
72}
73
74// kubernetesSetupUser ensures that for a given SSO username we:
75// - have a personal-<username> namespace
76// - have a sso:<username>:personal rolebinding that binds
77// system:admin-namespace to the user within their personal namespace
78// - have a sso:<username>:global clusterrolebinding that binds
79// system:viewer to the user at cluster level
Serge Bazanski0754ed82020-11-27 09:42:59 +000080func (p *prodvider) kubernetesSetupUser(ctx context.Context, username string) error {
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020081 namespace := "personal-" + username
Serge Bazanski0754ed82020-11-27 09:42:59 +000082 if err := p.ensureNamespace(ctx, namespace); err != nil {
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020083 return err
84 }
Serge Bazanski0754ed82020-11-27 09:42:59 +000085 if err := p.ensureRoleBindingPersonal(ctx, namespace, username); err != nil {
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020086 return err
87 }
Serge Bazanski0754ed82020-11-27 09:42:59 +000088 if err := p.ensureClusterRoleBindingGlobal(ctx, username); err != nil {
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020089 return err
90 }
91
92 return nil
93}
94
Serge Bazanski0754ed82020-11-27 09:42:59 +000095func (p *prodvider) ensureNamespace(ctx context.Context, name string) error {
96 _, err := p.k8s.CoreV1().Namespaces().Get(ctx, name, metav1.GetOptions{})
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020097 switch {
98 case err == nil:
99 // Already exists, nothing to do
100 return nil
101 case errors.IsNotFound(err):
102 break
103 default:
104 // Something went wrong.
105 return err
106 }
107 ns := &corev1.Namespace{
108 ObjectMeta: metav1.ObjectMeta{
109 Name: name,
110 },
111 }
Serge Bazanski0754ed82020-11-27 09:42:59 +0000112 _, err = p.k8s.CoreV1().Namespaces().Create(ctx, ns, metav1.CreateOptions{})
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +0200113 return err
114}
115
Serge Bazanski0754ed82020-11-27 09:42:59 +0000116func (p *prodvider) ensureRoleBindingPersonal(ctx context.Context, namespace, username string) error {
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +0200117 name := "sso:" + username + ":personal"
118 rb := &rbacv1.RoleBinding{
119 ObjectMeta: metav1.ObjectMeta{
120 Name: name,
121 Namespace: namespace,
122 },
123 Subjects: []rbacv1.Subject{
124 {
125 APIGroup: "rbac.authorization.k8s.io",
126 Kind: "User",
127 Name: username + "@hackerspace.pl",
128 },
129 },
130 RoleRef: rbacv1.RoleRef{
131 APIGroup: "rbac.authorization.k8s.io",
132 Kind: "ClusterRole",
133 Name: "system:admin-namespace",
134 },
135 }
136
137 rbs := p.k8s.RbacV1().RoleBindings(namespace)
Serge Bazanski0754ed82020-11-27 09:42:59 +0000138 _, err := rbs.Get(ctx, name, metav1.GetOptions{})
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +0200139 switch {
140 case err == nil:
141 // Already exists, update.
Serge Bazanski0754ed82020-11-27 09:42:59 +0000142 _, err = rbs.Update(ctx, rb, metav1.UpdateOptions{})
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +0200143 return err
144 case errors.IsNotFound(err):
145 // Create.
Serge Bazanski0754ed82020-11-27 09:42:59 +0000146 _, err = rbs.Create(ctx, rb, metav1.CreateOptions{})
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +0200147 return err
148 default:
149 // Something went wrong.
150 return err
151 }
152}
153
Serge Bazanski0754ed82020-11-27 09:42:59 +0000154func (p *prodvider) ensureClusterRoleBindingGlobal(ctx context.Context, username string) error {
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +0200155 name := "sso:" + username + ":global"
156 rb := &rbacv1.ClusterRoleBinding{
157 ObjectMeta: metav1.ObjectMeta{
158 Name: name,
159 },
160 Subjects: []rbacv1.Subject{
161 {
162 APIGroup: "rbac.authorization.k8s.io",
163 Kind: "User",
164 Name: username + "@hackerspace.pl",
165 },
166 },
167 RoleRef: rbacv1.RoleRef{
168 APIGroup: "rbac.authorization.k8s.io",
169 Kind: "ClusterRole",
170 Name: "system:viewer",
171 },
172 }
173
174 crbs := p.k8s.RbacV1().ClusterRoleBindings()
Serge Bazanski0754ed82020-11-27 09:42:59 +0000175 _, err := crbs.Get(ctx, name, metav1.GetOptions{})
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +0200176 switch {
177 case err == nil:
178 // Already exists, update.
Serge Bazanski0754ed82020-11-27 09:42:59 +0000179 _, err = crbs.Update(ctx, rb, metav1.UpdateOptions{})
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +0200180 return err
181 case errors.IsNotFound(err):
182 // Create.
Serge Bazanski0754ed82020-11-27 09:42:59 +0000183 _, err = crbs.Create(ctx, rb, metav1.CreateOptions{})
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +0200184 return err
185 default:
186 // Something went wrong.
187 return err
188 }
189}