blob: 1b396ffd64bdf080f7180e54fc47e8900b1f8710 [file] [log] [blame]
giolekvab64297c2021-12-13 14:36:32 +04001package main
2
3import (
4 "context"
5 "crypto"
6 "crypto/ed25519"
7 "crypto/rand"
8 "errors"
9 "fmt"
10
11 "inet.af/netaddr"
12
13 "github.com/slackhq/nebula/cert"
14
15 corev1 "k8s.io/api/core/v1"
16 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17 "k8s.io/client-go/kubernetes"
18
19 nebulav1 "github.com/giolekva/pcloud/core/nebula/controller/apis/nebula/v1"
20 clientset "github.com/giolekva/pcloud/core/nebula/controller/generated/clientset/versioned"
21)
22
23type nebulaCA struct {
24 Name string
25 Namespace string
26 Nodes []nebulaNode
27}
28
29type nebulaNode struct {
30 Name string
31 Namespace string
32 IP string
33}
34
35type Manager struct {
36 kubeClient kubernetes.Interface
37 nebulaClient clientset.Interface
38 namespace string
giolekvaf58a7692021-12-15 18:05:39 +040039 caName string
giolekvab64297c2021-12-13 14:36:32 +040040}
41
42func (m *Manager) ListAll() ([]*nebulaCA, error) {
43 ret := make([]*nebulaCA, 0)
44 cas, err := m.nebulaClient.LekvaV1().NebulaCAs("").List(context.TODO(), metav1.ListOptions{})
45 if err != nil {
46 return nil, err
47 }
48 for _, ca := range cas.Items {
49 ret = append(ret, &nebulaCA{
50 Name: ca.Name,
51 Namespace: ca.Namespace,
52 Nodes: make([]nebulaNode, 0),
53 })
54 }
55 nodes, err := m.nebulaClient.LekvaV1().NebulaNodes("").List(context.TODO(), metav1.ListOptions{})
56 if err != nil {
57 return nil, err
58 }
59 for _, node := range nodes.Items {
60 for _, ca := range ret {
61 if ca.Name == node.Spec.CAName {
62 ca.Nodes = append(ca.Nodes, nebulaNode{
63 Name: node.Name,
64 Namespace: node.Namespace,
65 IP: node.Spec.IPCidr,
66 })
67 }
68 }
69 }
70 return ret, nil
71}
72
73func (m *Manager) CreateNode(namespace, name, caNamespace, caName, ipCidr, pubKey string) (string, string, error) {
74 node := &nebulav1.NebulaNode{
75 ObjectMeta: metav1.ObjectMeta{
76 Name: name,
77 Namespace: namespace,
78 },
79 Spec: nebulav1.NebulaNodeSpec{
80 CAName: caName,
81 CANamespace: caNamespace,
82 IPCidr: ipCidr,
83 PubKey: pubKey,
84 SecretName: fmt.Sprintf("%s-cert", name),
85 },
86 }
87 node, err := m.nebulaClient.LekvaV1().NebulaNodes(namespace).Create(context.TODO(), node, metav1.CreateOptions{})
88 if err != nil {
89 return "", "", err
90 }
91 return node.Namespace, node.Name, nil
92}
93
94func (m *Manager) GetNodeCertQR(namespace, name string) ([]byte, error) {
95 secret, err := m.getNodeSecret(namespace, name)
96 if err != nil {
97 return nil, err
98 }
99 return secret.Data["host.png"], nil
100}
101
102func (m *Manager) GetCACertQR(namespace, name string) ([]byte, error) {
giolekvaf58a7692021-12-15 18:05:39 +0400103 secret, err := m.getCASecret(namespace, name)
giolekvab64297c2021-12-13 14:36:32 +0400104 if err != nil {
105 return nil, err
106 }
107 return secret.Data["ca.png"], nil
108}
109
110func (m *Manager) getNextIP() (netaddr.IP, error) {
111 nodes, err := m.nebulaClient.LekvaV1().NebulaNodes(m.namespace).List(context.TODO(), metav1.ListOptions{})
112 if err != nil {
113 return netaddr.IP{}, err
114 }
115 var max netaddr.IP
116 for _, node := range nodes.Items {
117 ip := netaddr.MustParseIPPrefix(node.Spec.IPCidr)
118 if max.Less(ip.IP()) {
119 max = ip.IP()
120 }
121 }
122 n := max.Next()
123 if n.IsZero() {
124 return n, errors.New("IP address range exhausted")
125 }
126 return n, nil
127}
128
129func (m *Manager) Sign(message []byte) ([]byte, error) {
giolekvaf58a7692021-12-15 18:05:39 +0400130 secret, err := m.getCASecret(m.namespace, m.caName)
giolekvab64297c2021-12-13 14:36:32 +0400131 if err != nil {
132 return nil, err
133 }
134 edPriv, _, err := cert.UnmarshalEd25519PrivateKey(secret.Data["ca.key"])
135 if err != nil {
136 return nil, err
137 }
138 return edPriv.Sign(rand.Reader, message, crypto.Hash(0))
139}
140
141func (m *Manager) VerifySignature(message, signature []byte) (bool, error) {
giolekvaf58a7692021-12-15 18:05:39 +0400142 secret, err := m.getCASecret(m.namespace, m.caName)
giolekvab64297c2021-12-13 14:36:32 +0400143 if err != nil {
144 return false, err
145 }
146 edPriv, _, err := cert.UnmarshalEd25519PrivateKey(secret.Data["ca.key"])
147 if err != nil {
148 return false, err
149 }
150 return ed25519.Verify(edPriv.Public().(ed25519.PublicKey), message, signature), nil
151}
152
giolekvaf58a7692021-12-15 18:05:39 +0400153func (m *Manager) getCASecret(namespace, name string) (*corev1.Secret, error) {
154 ca, err := m.nebulaClient.LekvaV1().NebulaCAs(namespace).Get(context.TODO(), name, metav1.GetOptions{})
155 if err != nil {
156 return nil, err
157 }
158 return m.kubeClient.CoreV1().Secrets(namespace).Get(context.TODO(), ca.Spec.SecretName, metav1.GetOptions{})
159}
160
giolekvab64297c2021-12-13 14:36:32 +0400161func (m *Manager) getNodeSecret(namespace, name string) (*corev1.Secret, error) {
162 node, err := m.nebulaClient.LekvaV1().NebulaNodes(namespace).Get(context.TODO(), name, metav1.GetOptions{})
163 if err != nil {
164 return nil, err
165 }
166 return m.kubeClient.CoreV1().Secrets(namespace).Get(context.TODO(), node.Spec.SecretName, metav1.GetOptions{})
167}