Skip to content

Commit d82f42c

Browse files
authored
[aux] Add CAs endpoint (#1194)
1 parent 12e2538 commit d82f42c

File tree

13 files changed

+275
-7
lines changed

13 files changed

+275
-7
lines changed

deploy/operations/certificates-management/ca_pool.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ def regenerate_ca_files(cluster):
7474
f.write("\n\n".join(CAs))
7575

7676
shutil.copy(cluster.ca_pool_ca, cluster.client_ca)
77+
shutil.copy(cluster.ca_cert_file, cluster.client_instance_ca)
7778

7879
for node_type in ["master", "tserver"]:
7980
shutil.copy(cluster.ca_pool_ca, getattr(cluster, f"{node_type}_ca"))

deploy/operations/certificates-management/cluster.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ def client_certs_dir(self):
5151
def client_ca(self):
5252
return os.path.join(self.client_certs_dir, "root.crt")
5353

54+
@property
55+
def client_instance_ca(self):
56+
return os.path.join(self.client_certs_dir, "ca-instance.crt")
57+
5458
@property
5559
def master_certs_dir(self):
5660
return os.path.join(self.directory, "masters")

deploy/services/helm-charts/dss/templates/_volumes.tpl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
- mountPath: /opt/yugabyte-certs/ca.crt
1414
name: ca-certs
1515
subPath: root.crt
16+
- mountPath: /opt/yugabyte-certs/ca-instance.crt
17+
name: ca-certs
18+
subPath: ca-instance.crt
1619
{{- end -}}
1720
{{- end -}}
1821
{{- define "client-certs:volume" -}}

interfaces/aux_/aux_.yaml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,15 @@ components:
8484
- timestamp
8585
- source
8686

87+
CAsResponse:
88+
type: object
89+
properties:
90+
CAs:
91+
description: A list of certificates, each in PEM format.
92+
type: array
93+
items:
94+
type: string
95+
8796
paths:
8897
/aux/v1/version:
8998
get:
@@ -208,6 +217,44 @@ paths:
208217
application/json:
209218
schema:
210219
$ref: '#/components/schemas/ErrorResponse'
220+
/aux/v1/configuration/accepted_ca_certs:
221+
get:
222+
summary: Current certificates of certificate authorities (CAs) that this DSS instance accepts as legitimate signers of node certificates for the pool of DSS instances constituting the DSS Airspace Representation.
223+
operationId: getAcceptedCAs
224+
tags: [ dss ]
225+
responses:
226+
'200':
227+
description: The information is successfully returned.
228+
content:
229+
application/json:
230+
schema:
231+
$ref: '#/components/schemas/CAsResponse'
232+
'501':
233+
description: >-
234+
The server has not implemented this operation.
235+
content:
236+
application/json:
237+
schema:
238+
$ref: '#/components/schemas/ErrorResponse'
239+
/aux/v1/configuration/ca_certs:
240+
get:
241+
summary: Current certificates of certificate authorities (CAs) that signed the node certificates for this DSS instance. May return more that one certificate (e.g. for rotations). Other DSS instances in the pool should accept node certificates signed by these CAs.
242+
operationId: getInstanceCAs
243+
tags: [ dss ]
244+
responses:
245+
'200':
246+
description: The information is successfully returned.
247+
content:
248+
application/json:
249+
schema:
250+
$ref: '#/components/schemas/CAsResponse'
251+
'501':
252+
description: >-
253+
The server has not implemented this operation.
254+
content:
255+
application/json:
256+
schema:
257+
$ref: '#/components/schemas/ErrorResponse'
211258
security:
212259
- Auth:
213260
- dss.read.identification_service_areas

pkg/api/auxv1/interface.gen.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import (
77
)
88

99
var (
10+
InterussPoolStatusReadScope = api.RequiredScope("interuss.pool_status.read")
1011
DssWriteIdentificationServiceAreasScope = api.RequiredScope("dss.write.identification_service_areas")
1112
DssReadIdentificationServiceAreasScope = api.RequiredScope("dss.read.identification_service_areas")
12-
InterussPoolStatusReadScope = api.RequiredScope("interuss.pool_status.read")
1313
GetVersionSecurity = []api.AuthorizationOption{}
1414
ValidateOauthSecurity = []api.AuthorizationOption{
1515
{
@@ -29,6 +29,8 @@ var (
2929
"Auth": {InterussPoolStatusReadScope},
3030
},
3131
}
32+
GetAcceptedCAsSecurity = []api.AuthorizationOption{}
33+
GetInstanceCAsSecurity = []api.AuthorizationOption{}
3234
)
3335

3436
type GetVersionRequest struct {
@@ -106,6 +108,36 @@ type GetDSSInstancesResponseSet struct {
106108
Response500 *api.InternalServerErrorBody
107109
}
108110

111+
type GetAcceptedCAsRequest struct {
112+
// The result of attempting to authorize this request
113+
Auth api.AuthorizationResult
114+
}
115+
type GetAcceptedCAsResponseSet struct {
116+
// The information is successfully returned.
117+
Response200 *CAsResponse
118+
119+
// The server has not implemented this operation.
120+
Response501 *ErrorResponse
121+
122+
// Auto-generated internal server error response
123+
Response500 *api.InternalServerErrorBody
124+
}
125+
126+
type GetInstanceCAsRequest struct {
127+
// The result of attempting to authorize this request
128+
Auth api.AuthorizationResult
129+
}
130+
type GetInstanceCAsResponseSet struct {
131+
// The information is successfully returned.
132+
Response200 *CAsResponse
133+
134+
// The server has not implemented this operation.
135+
Response501 *ErrorResponse
136+
137+
// Auto-generated internal server error response
138+
Response500 *api.InternalServerErrorBody
139+
}
140+
109141
type Implementation interface {
110142
// Queries the version of the DSS.
111143
GetVersion(ctx context.Context, req *GetVersionRequest) GetVersionResponseSet
@@ -118,4 +150,10 @@ type Implementation interface {
118150

119151
// Queries the current information for DSS instances participating in the pool.
120152
GetDSSInstances(ctx context.Context, req *GetDSSInstancesRequest) GetDSSInstancesResponseSet
153+
154+
// Current certificates of certificate authorities (CAs) that this DSS instance accepts as legitimate signers of node certificates for the pool of DSS instances constituting the DSS Airspace Representation.
155+
GetAcceptedCAs(ctx context.Context, req *GetAcceptedCAsRequest) GetAcceptedCAsResponseSet
156+
157+
// Current certificates of certificate authorities (CAs) that signed the node certificates for this DSS instance. May return more that one certificate (e.g. for rotations). Other DSS instances in the pool should accept node certificates signed by these CAs.
158+
GetInstanceCAs(ctx context.Context, req *GetInstanceCAsRequest) GetInstanceCAsResponseSet
121159
}

pkg/api/auxv1/server.gen.go

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,62 @@ func (s *APIRouter) GetDSSInstances(exp *regexp.Regexp, w http.ResponseWriter, r
157157
api.WriteJSON(w, 500, api.InternalServerErrorBody{ErrorMessage: "Handler implementation did not set a response"})
158158
}
159159

160+
func (s *APIRouter) GetAcceptedCAs(exp *regexp.Regexp, w http.ResponseWriter, r *http.Request) {
161+
var req GetAcceptedCAsRequest
162+
163+
// Authorize request
164+
req.Auth = s.Authorizer.Authorize(w, r, GetAcceptedCAsSecurity)
165+
166+
// Call implementation
167+
ctx, cancel := context.WithCancel(r.Context())
168+
defer cancel()
169+
response := s.Implementation.GetAcceptedCAs(ctx, &req)
170+
171+
// Write response to client
172+
if response.Response200 != nil {
173+
api.WriteJSON(w, 200, response.Response200)
174+
return
175+
}
176+
if response.Response501 != nil {
177+
api.WriteJSON(w, 501, response.Response501)
178+
return
179+
}
180+
if response.Response500 != nil {
181+
api.WriteJSON(w, 500, response.Response500)
182+
return
183+
}
184+
api.WriteJSON(w, 500, api.InternalServerErrorBody{ErrorMessage: "Handler implementation did not set a response"})
185+
}
186+
187+
func (s *APIRouter) GetInstanceCAs(exp *regexp.Regexp, w http.ResponseWriter, r *http.Request) {
188+
var req GetInstanceCAsRequest
189+
190+
// Authorize request
191+
req.Auth = s.Authorizer.Authorize(w, r, GetInstanceCAsSecurity)
192+
193+
// Call implementation
194+
ctx, cancel := context.WithCancel(r.Context())
195+
defer cancel()
196+
response := s.Implementation.GetInstanceCAs(ctx, &req)
197+
198+
// Write response to client
199+
if response.Response200 != nil {
200+
api.WriteJSON(w, 200, response.Response200)
201+
return
202+
}
203+
if response.Response501 != nil {
204+
api.WriteJSON(w, 501, response.Response501)
205+
return
206+
}
207+
if response.Response500 != nil {
208+
api.WriteJSON(w, 500, response.Response500)
209+
return
210+
}
211+
api.WriteJSON(w, 500, api.InternalServerErrorBody{ErrorMessage: "Handler implementation did not set a response"})
212+
}
213+
160214
func MakeAPIRouter(impl Implementation, auth api.Authorizer) APIRouter {
161-
router := APIRouter{Implementation: impl, Authorizer: auth, Routes: make([]*api.Route, 4)}
215+
router := APIRouter{Implementation: impl, Authorizer: auth, Routes: make([]*api.Route, 6)}
162216

163217
pattern := regexp.MustCompile("^/aux/v1/version$")
164218
router.Routes[0] = &api.Route{Method: http.MethodGet, Pattern: pattern, Handler: router.GetVersion}
@@ -172,5 +226,11 @@ func MakeAPIRouter(impl Implementation, auth api.Authorizer) APIRouter {
172226
pattern = regexp.MustCompile("^/aux/v1/pool/dss_instances$")
173227
router.Routes[3] = &api.Route{Method: http.MethodGet, Pattern: pattern, Handler: router.GetDSSInstances}
174228

229+
pattern = regexp.MustCompile("^/aux/v1/configuration/accepted_ca_certs$")
230+
router.Routes[4] = &api.Route{Method: http.MethodGet, Pattern: pattern, Handler: router.GetAcceptedCAs}
231+
232+
pattern = regexp.MustCompile("^/aux/v1/configuration/ca_certs$")
233+
router.Routes[5] = &api.Route{Method: http.MethodGet, Pattern: pattern, Handler: router.GetInstanceCAs}
234+
175235
return router
176236
}

pkg/api/auxv1/types.gen.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,8 @@ type Heartbeat struct {
4444
// The time by which a new heartbeat should be registered for this DSS instance if the DSS instance operator's system is behaving correctly.
4545
NextHeartbeatExpectedBefore *string `json:"next_heartbeat_expected_before,omitempty"`
4646
}
47+
48+
type CAsResponse struct {
49+
// A list of certificates, each in PEM format.
50+
Cas *[]string `json:"CAs,omitempty"`
51+
}

pkg/api/ridv1/interface.gen.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import (
77
)
88

99
var (
10-
DssWriteIdentificationServiceAreasScope = api.RequiredScope("dss.write.identification_service_areas")
1110
DssReadIdentificationServiceAreasScope = api.RequiredScope("dss.read.identification_service_areas")
11+
DssWriteIdentificationServiceAreasScope = api.RequiredScope("dss.write.identification_service_areas")
1212
SearchIdentificationServiceAreasSecurity = []api.AuthorizationOption{
1313
{
1414
"AuthFromAuthorizationAuthority": {DssReadIdentificationServiceAreasScope},

pkg/api/scdv1/interface.gen.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import (
88

99
var (
1010
UtmAvailabilityArbitrationScope = api.RequiredScope("utm.availability_arbitration")
11-
UtmStrategicCoordinationScope = api.RequiredScope("utm.strategic_coordination")
11+
UtmConstraintProcessingScope = api.RequiredScope("utm.constraint_processing")
1212
UtmConstraintManagementScope = api.RequiredScope("utm.constraint_management")
13+
UtmStrategicCoordinationScope = api.RequiredScope("utm.strategic_coordination")
1314
UtmConformanceMonitoringSaScope = api.RequiredScope("utm.conformance_monitoring_sa")
14-
UtmConstraintProcessingScope = api.RequiredScope("utm.constraint_processing")
1515
QueryOperationalIntentReferencesSecurity = []api.AuthorizationOption{
1616
{
1717
"Authority": {UtmStrategicCoordinationScope},

pkg/aux_/accepted_ca_certs.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package aux
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
"strings"
8+
9+
restapi "github.com/interuss/dss/pkg/api/auxv1"
10+
"github.com/interuss/dss/pkg/datastore/flags"
11+
)
12+
13+
func (a *Server) GetAcceptedCAs(ctx context.Context, req *restapi.GetAcceptedCAsRequest) restapi.GetAcceptedCAsResponseSet {
14+
15+
connectParameters := flags.ConnectParameters()
16+
17+
CAFile := connectParameters.GetCAFile()
18+
19+
if CAFile == "" {
20+
return restapi.GetAcceptedCAsResponseSet{Response200: &restapi.CAsResponse{}}
21+
}
22+
23+
data, err := os.ReadFile(CAFile)
24+
25+
if err != nil {
26+
msg := fmt.Sprintf("Unable to read CA certificate file for accepted CAs, did try to read '%s', got: %s", CAFile, err)
27+
return restapi.GetAcceptedCAsResponseSet{Response501: &restapi.ErrorResponse{Message: &msg}}
28+
}
29+
30+
var CAs []string
31+
32+
for _, CA := range strings.Split(string(data), START_OF_CERTIFICATE) {
33+
CA = strings.Trim(CA, "\r\n")
34+
35+
if CA != "" {
36+
// Re-add the start mark that was removed by Split()
37+
CA = START_OF_CERTIFICATE + "\n" + CA
38+
CAs = append(CAs, CA)
39+
}
40+
}
41+
42+
return restapi.GetAcceptedCAsResponseSet{Response200: &restapi.CAsResponse{Cas: &CAs}}
43+
}

0 commit comments

Comments
 (0)