Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,18 @@ By default, ratsd core listens on port 8895. Use `POST /ratsd/chares` to retriev
$ curl -X POST http://localhost:8895/ratsd/chares -H "Content-type: application/vnd.veraison.chares+json" -d '{"nonce": "TUlEQk5IMjhpaW9pc2pQeXh4eHh4eHh4eHh4eHh4eHhNSURCTkgyOGlpb2lzalB5eHh4eHh4eHh4eHh4eHh4eA"}'
{"cmw":"eyJfX2Ntd2NfdCI6InRhZzpnaXRodWIuY29tLDIwMjU6dmVyYWlzb24vcmF0c2QvY213IiwibW9jay10c20iOlsiYXBwbGljYXRpb24vdm5kLnZlcmFpc29uLmNvbmZpZ2ZzLXRzbStqc29uIiwiZXlKaGRYaGliRzlpSWpvaVdWaFdORmx0ZUhaWlp5SXNJbTkxZEdKc2IySWlPaUpqU0Vwd1pHMTRiR1J0Vm5OUGFVRjNRMjFzZFZsdGVIWlphbTluVGtkUk1FOVVVVEJPUkVrd1dsUlJORTE2U1hwUFJGazFUbXByTWxwcVdUVk9lazB5V1ZSVmQwNTZhek5QUkdNMFRucG5NMDlFWXpST2VtY3pUMFJqTkU1Nlp6TlBSR00wVG5wbk0wOUVZelJPZW1jelQwUlNhMDVFYXpCT1JGRjVUa2RWTUU5RVRYbE5lbWN5VDFSWk5VNXRXVEpQVkdONlRtMUZNVTFFWXpWT2VtY3pUMFJqTkU1Nlp6TlBSR00wVG5wbk0wOUVZelJPZW1jelQwUmpORTU2WnpOUFJHTTBUbnBuSWl3aWNISnZkbWxrWlhJaU9pSm1ZV3RsWEc0aWZRIl19","eat_nonce":"TUlEQk5IMjhpaW9pc2pQeXh4eHh4eHh4eHh4eHh4eHhNSURCTkgyOGlpb2lzalB5eHh4eHh4eHh4eHh4eHh4eA","eat_profile":"tag:github.com,2024:veraison/ratsd"}
```
## Get available attesters
Use endpoint `GET /ratsd/subattesters` to query all available leaf attesters and their available options. The usage can be found in the following
```console
$ curl http://localhost:8895/ratsd/subattesters
[{"name":"mock-tsm","options":[{"data-type":"string","name":"privilege_level"}]},{"name":"tsm-report","options":[{"data-type":"string","name":"privilege_level"}]}]
```
## Complex queries

Ratsd currently supports the Trusted Secure Module `tsm` attester. You can specify the `privilege_level` for configfs-TSM in the query.
```bash
curl -X POST http://localhost:8895/ratsd/chares -H "Content-type: application/vnd.veraison.chares+json" -d '{"nonce": "TUlEQk5IMjhpaW9pc2pQeXh4eHh4eHh4eHh4eHh4eHhNSURCTkgyOGlpb2lzalB5eHh4eHh4eHh4eHh4eHh4eA", tsm-report:{"privilege_level": "$level"}}' # Replace $level with a number from 0 to 3
```

## Get evidence from the selected attester only

If more than one leaf attesters present, ratsd adds the evidence generated by all attesters to the response of `/ratsd/chares`. To limit the output to the selected attester, add `list-options: selected` to config.yaml,
Expand Down
74 changes: 60 additions & 14 deletions api/api.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
// Defines missing consts in the API Spec
const (
ApplicationvndVeraisonCharesJson string = "application/vnd.veraison.chares+json"
JsonType string = "application/json"
)

type Server struct {
Expand Down Expand Up @@ -211,3 +212,31 @@ func (s *Server) RatsdChares(w http.ResponseWriter, r *http.Request, param Ratsd
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(eat)
}

func (s *Server) RatsdSubattesters(w http.ResponseWriter, r *http.Request) {
resp := []SubAttester{}

pl := s.manager.GetPluginList()
for _, pn := range pl {
options := new([]Option)
attester, err := s.manager.LookupByName(pn)
if err != nil {
errMsg := fmt.Sprintf(
"failed to get handle from %s: %s", pn, err.Error())
p := problems.NewDetailedProblem(http.StatusInternalServerError, errMsg)
s.reportProblem(w, p)
return
}

for _, o := range attester.GetOptions().Options {
option := Option{Name: o.Name, DataType: OptionDataType(o.Type)}
*options = append(*options, option)
}
entry := SubAttester{Name: pn, Options: options,}
resp = append(resp, entry)
}

w.Header().Set("Content-Type", JsonType)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(resp)
}
49 changes: 49 additions & 0 deletions api/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
mock_deps "github.com/veraison/ratsd/api/mocks"
"github.com/veraison/ratsd/attesters/mocktsm"
"github.com/veraison/ratsd/tokens"
"github.com/veraison/ratsd/attesters/tsm"
"github.com/veraison/services/log"
)

Expand All @@ -27,6 +28,54 @@ const (
validNonce = "TUlEQk5IMjhpaW9pc2pQeXh4eHh4eHh4eHh4eHh4eHhNSURCTkgyOGlpb2lzalB5eHh4eHh4eHh4eHh4eHh4eA"
)

func TestRatsdSubattesters_valid_requests(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

dm := mock_deps.NewMockIManager(ctrl)
dm.EXPECT().GetPluginList().Return([]string{}).Times(1)
dm.EXPECT().GetPluginList().Return([]string{"mock-tsm"}).Times(1)
dm.EXPECT().GetPluginList().Return([]string{"mock-tsm","tsm-report"}).Times(1)
dm.EXPECT().LookupByName("mock-tsm").Return(mocktsm.GetPlugin(), nil).AnyTimes()
dm.EXPECT().LookupByName("tsm-report").Return(&tsm.TSMPlugin{}, nil).AnyTimes()
logger := log.Named("test")
s := NewServer(logger, dm, "all")
tests := []struct {
name, response string
}{
{
"no attester",
"[]\n",
},
{
"with only mocktsm attester",
"[{\"name\":\"mock-tsm\",\"options\":[{\"data-type\":\"string\",\"name\":\"privilege_level\"}]}]\n",
},
{
"with tsm and mocktsm attester",
"[{\"name\":\"mock-tsm\",\"options\":[{\"data-type\":\"string\",\"name\":\"privilege_level\"}]},{\"name\":\"tsm-report\",\"options\":[{\"data-type\":\"string\",\"name\":\"privilege_level\"}]}]\n",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := httptest.NewRecorder()
rb := strings.NewReader(tt.response)
r, _ := http.NewRequest(http.MethodGet, "/ratsd/subattesters", rb)
s.RatsdSubattesters(w, r)

expectedCode := http.StatusOK
expectedType := jsonType
expectedBody := tt.response

assert.Equal(t, expectedCode, w.Code)
assert.Equal(t, expectedType, w.Result().Header.Get("Content-Type"))
assert.Equal(t, expectedBody, w.Body.String())
})
}

}

func TestRatsdChares_wrong_content_type(t *testing.T) {
expectedCode := http.StatusBadRequest
expectedType := problems.ProblemMediaType
Expand Down
12 changes: 12 additions & 0 deletions attesters/mocktsm/mocktsm.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,18 @@ func getEvidenceError(e error) *compositor.EvidenceOut {
}
}

func (m *MockPlugin) GetOptions() *compositor.OptionsOut {
options := []*compositor.Option{
&compositor.Option{Name: "privilege_level", Type: "string"},
}

return &compositor.OptionsOut{
Options: options,
Status: statusSucceeded,
}

}

func (m *MockPlugin) GetSubAttesterID() *compositor.SubAttesterIDOut {
return &compositor.SubAttesterIDOut{
SubAttesterID: sid,
Expand Down
13 changes: 13 additions & 0 deletions attesters/mocktsm/mocktsm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ var (
p = GetPlugin()
)

func Test_GetOptions(t *testing.T) {
options := []*compositor.Option{
&compositor.Option{Name: "privilege_level", Type: "string"},
}

expected := &compositor.OptionsOut{
Options: options,
Status: statusSucceeded,
}

assert.Equal(t, expected, p.GetOptions())
}

func Test_GetSubAttesterID(t *testing.T) {
expected := &compositor.SubAttesterIDOut{
SubAttesterID: sid,
Expand Down
11 changes: 11 additions & 0 deletions attesters/tsm/tsm.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,17 @@ func getEvidenceError(e error) *compositor.EvidenceOut {
}
}

func (t *TSMPlugin) GetOptions() *compositor.OptionsOut {
options := []*compositor.Option{
&compositor.Option{Name: "privilege_level", Type: "string"},
}

return &compositor.OptionsOut{
Options: options,
Status: statusSucceeded,
}
}

func (t *TSMPlugin) GetSubAttesterID() *compositor.SubAttesterIDOut {
return &compositor.SubAttesterIDOut{
SubAttesterID: sid,
Expand Down
13 changes: 13 additions & 0 deletions attesters/tsm/tsm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,19 @@ func Test_getEvidenceError(t *testing.T) {
assert.Equal(t, expected, getEvidenceError(e))
}

func Test_GetOptions(t *testing.T) {
options := []*compositor.Option{
&compositor.Option{Name: "privilege_level", Type: "string"},
}

expected := &compositor.OptionsOut{
Options: options,
Status: statusSucceeded,
}

assert.Equal(t, expected, p.GetOptions())
}

func Test_GetSubAttesterID(t *testing.T) {
expected := &compositor.SubAttesterIDOut{
SubAttesterID: sid,
Expand Down
41 changes: 41 additions & 0 deletions docs/api/ratsd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,19 @@ paths:
$ref: '#/components/schemas/ChaResRequest'
security:
- BearerAuth: []
/ratsd/subattesters:
get:
description: Get a list of the sub-attesters available on this machine.
operationId: Ratsd_subattesters
responses:
'200':
description: The request has succeeded.
content:
application/vnd.veraison.attesters+json:
schema:
type: array
items:
$ref: '#/components/schemas/SubAttester'
components:
parameters:
ChaResRequestParameters.accept:
Expand Down Expand Up @@ -107,6 +120,23 @@ components:
- tag:github.com,2024:veraison/ratsd
nested-token:
$ref: '#/components/schemas/CMW'
Option:
type: object
required:
- name
- data-type
properties:
name:
type: string
data-type:
type: string
enum:
- string
- number
- integer
- boolean
- array
- object
ProblemDetails:
type: object
properties:
Expand All @@ -120,6 +150,17 @@ components:
type: string
instance:
type: string
SubAttester:
type: object
required:
- name
properties:
name:
type: string
options:
type: array
items:
$ref: '#/components/schemas/Option'
UnauthorizedError:
type: object
required:
Expand Down
Loading
Loading