blob: 4e1e5faf615356aa7a2d1c82b20803c9093ea563 [file] [log] [blame]
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +02001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package api
18
19import (
Lorenz Brunaa6b7342019-12-12 02:55:02 +010020 "context"
21 "crypto/ed25519"
22 "crypto/rand"
23 "crypto/tls"
24 "crypto/x509"
25 "crypto/x509/pkix"
26 "errors"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020027 "fmt"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010028 "math/big"
29 "net"
30 "time"
31
32 "git.monogon.dev/source/nexantic.git/core/generated/api"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020033 schema "git.monogon.dev/source/nexantic.git/core/generated/api"
34 "git.monogon.dev/source/nexantic.git/core/internal/common"
Leopold Schabel68c58752019-11-14 21:00:59 +010035 "git.monogon.dev/source/nexantic.git/core/internal/common/service"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020036 "git.monogon.dev/source/nexantic.git/core/internal/consensus"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010037 "go.etcd.io/etcd/clientv3"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020038 "go.uber.org/zap"
39 "google.golang.org/grpc"
Leopold Schabela4516f92019-12-04 20:27:05 +000040 "google.golang.org/grpc/reflection"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010041 "google.golang.org/grpc/credentials"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020042)
43
44type (
45 Server struct {
Leopold Schabel68c58752019-11-14 21:00:59 +010046 *service.BaseService
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020047
Lorenz Brunaa6b7342019-12-12 02:55:02 +010048 grpcServer *grpc.Server
49 externalGrpcServer *grpc.Server
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020050
51 consensusService *consensus.Service
52
53 config *Config
54 }
55
56 Config struct {
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020057 }
58)
59
Lorenz Brunaa6b7342019-12-12 02:55:02 +010060var (
61 // From RFC 5280 Section 4.1.2.5
62 unknownNotAfter = time.Unix(253402300799, 0)
63)
64
65func NewApiServer(config *Config, logger *zap.Logger, consensusService *consensus.Service) (*Server, error) {
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020066 s := &Server{
67 config: config,
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020068 consensusService: consensusService,
69 }
70
Leopold Schabel68c58752019-11-14 21:00:59 +010071 s.BaseService = service.NewBaseService("api", logger, s)
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020072
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020073 return s, nil
74}
75
Lorenz Brunaa6b7342019-12-12 02:55:02 +010076func (s *Server) getStore() clientv3.KV {
77 // Cannot be moved to initialization because an internal reference will be nil
78 return s.consensusService.GetStore("api", "")
79}
80
81// BootstrapNewClusterHook creates the necessary key material for the API Servers and stores it in
82// the consensus service. It also creates a node entry for the initial node.
83func (s *Server) BootstrapNewClusterHook(initNodeReq *api.NewNodeInfo) error {
84 serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 127)
85 serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
86 if err != nil {
87 return fmt.Errorf("Failed to generate serial number: %w", err)
88 }
89
90 pubKey, privKeyRaw, err := ed25519.GenerateKey(rand.Reader)
91 if err != nil {
92 return err
93 }
94 privkey, err := x509.MarshalPKCS8PrivateKey(privKeyRaw)
95 if err != nil {
96 return err
97 }
98
99 // This has no SANs because it authenticates by public key, not by name
100 masterCert := &x509.Certificate{
101 SerialNumber: serialNumber,
102 Subject: pkix.Name{
103 CommonName: "Smalltown Master",
104 },
105 IsCA: false,
106 BasicConstraintsValid: true,
107 NotBefore: time.Now(),
108 NotAfter: unknownNotAfter,
109 // Certificate is used both as server & client
110 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
111 }
112 cert, err := x509.CreateCertificate(rand.Reader, masterCert, masterCert, pubKey, privKeyRaw)
113 if err != nil {
114 return err
115 }
116 store := s.getStore()
117 if _, err := store.Put(context.Background(), "master.der", string(cert)); err != nil {
118 return err
119 }
120 if _, err := store.Put(context.Background(), "master-key.der", string(privkey)); err != nil {
121 return err
122 }
123
124 // TODO: Further integrity providers need to be plumbed in here
125 node, err := s.TPM2BootstrapNode(initNodeReq)
126 if err != nil {
127 return err
128 }
129
130 if err := s.registerNewNode(node); err != nil {
131 return err
132 }
133 return nil
134}
135
136// GetMasterCert gets the master certificate in X.509 DER form
137// This is mainly used to issue enrolment configs
138func (s *Server) GetMasterCert() ([]byte, error) {
139 store := s.getStore()
140 res, err := store.Get(context.Background(), "master.der")
141 if err != nil {
142 return []byte{}, err
143 }
144 if len(res.Kvs) != 1 {
145 return []byte{}, errors.New("master certificate not found")
146 }
147 certRaw := res.Kvs[0].Value
148 return certRaw, nil
149}
150
151// TODO(lorenz): Move consensus/certificate interaction into a utility, is now duplicated too often
152func (s *Server) loadMasterCert() (*tls.Certificate, error) {
153
154 store := s.getStore()
155 var tlsCert tls.Certificate
156 res, err := store.Get(context.Background(), "master.der")
157 if err != nil {
158 return nil, err
159 }
160 if len(res.Kvs) != 1 {
161 return nil, errors.New("master certificate not found")
162 }
163 certRaw := res.Kvs[0].Value
164
165 tlsCert.Certificate = append(tlsCert.Certificate, certRaw)
166 tlsCert.Leaf, err = x509.ParseCertificate(certRaw)
167
168 res, err = store.Get(context.Background(), "master-key.der")
169 if err != nil {
170 return nil, err
171 }
172 if len(res.Kvs) != 1 {
173 return nil, errors.New("master certificate not found")
174 }
175 keyRaw := res.Kvs[0].Value
176 key, err := x509.ParsePKCS8PrivateKey(keyRaw)
177 if err != nil {
178 return nil, fmt.Errorf("failed to load master private key: %w", err)
179 }
180 edKey, ok := key.(ed25519.PrivateKey)
181 if !ok {
182 return nil, errors.New("invalid private key")
183 }
184 tlsCert.PrivateKey = edKey
185 return &tlsCert, nil
186}
187
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200188func (s *Server) OnStart() error {
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100189 masterListenHost := fmt.Sprintf(":%d", common.MasterServicePort)
190 lis, err := net.Listen("tcp", masterListenHost)
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200191 if err != nil {
192 s.Logger.Fatal("failed to listen", zap.Error(err))
193 }
194
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100195 externalListeneHost := fmt.Sprintf(":%d", common.ExternalServicePort)
196 externalListener, err := net.Listen("tcp", externalListeneHost)
197 if err != nil {
198 s.Logger.Fatal("failed to listen", zap.Error(err))
199 }
200
201 masterCert, err := s.loadMasterCert()
202 if err != nil {
203 s.Logger.Error("Failed to load Master Service Key Material: %w", zap.Error(err))
204 return err
205 }
206
207 masterTransportCredentials := credentials.NewServerTLSFromCert(masterCert)
208
209 masterGrpcServer := grpc.NewServer(grpc.Creds(masterTransportCredentials))
210 clusterManagementGrpcServer := grpc.NewServer()
211 schema.RegisterClusterManagementServer(clusterManagementGrpcServer, s)
212 schema.RegisterNodeManagementServiceServer(masterGrpcServer, s)
213
214 reflection.Register(masterGrpcServer)
215
216 s.grpcServer = masterGrpcServer
217 s.externalGrpcServer = clusterManagementGrpcServer
218
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200219 go func() {
220 err = s.grpcServer.Serve(lis)
221 s.Logger.Error("API server failed", zap.Error(err))
222 }()
223
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100224 go func() {
225 err = s.externalGrpcServer.Serve(externalListener)
226 s.Logger.Error("API server failed", zap.Error(err))
227 }()
228
229 s.Logger.Info("gRPC listening", zap.String("host", masterListenHost))
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200230
231 return nil
232}
233
234func (s *Server) OnStop() error {
235 s.grpcServer.Stop()
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100236 s.externalGrpcServer.Stop()
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200237
238 return nil
239}