Skip to content

Commit

Permalink
Cherry-pick and squash #908
Browse files Browse the repository at this point in the history
  • Loading branch information
oskirby committed Jun 22, 2024
1 parent f2a9222 commit 8ad7565
Show file tree
Hide file tree
Showing 15 changed files with 729 additions and 142 deletions.
7 changes: 1 addition & 6 deletions bin/run_integration_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,9 @@ while test "true" != "$(docker inspect -f {{.State.Running}} autograph-app-hsm)"
sleep 1 # wait before checking again
done

# fetch the updated root hash from the app-hsm service
docker cp autograph-app-hsm:/tmp/normandy_dev_root_hash.txt .
APP_HSM_NORMANDY_ROOT_HASH=$(grep '[0-9A-F]' normandy_dev_root_hash.txt | tr -d '\r\n')

# start the monitor lambda emulators
docker compose up -d monitor-lambda-emulator
AUTOGRAPH_ROOT_HASH=$APP_HSM_NORMANDY_ROOT_HASH docker compose up -d monitor-hsm-lambda-emulator
docker compose up -d monitor-hsm-lambda-emulator

echo "waiting for monitor-lambda-emulator to start"
while test "true" != "$(docker inspect -f {{.State.Running}} autograph-monitor-lambda-emulator)"; do
Expand All @@ -43,7 +39,6 @@ while test "true" != "$(docker inspect -f {{.State.Running}} autograph-monitor-h
sleep 1 # wait before checking again
done

echo "checking monitoring using hsm root hash:" "$APP_HSM_NORMANDY_ROOT_HASH"
# exec in containers to workaround https://circleci.com/docs/2.0/building-docker-images/#accessing-services
docker compose exec monitor-lambda-emulator "/usr/local/bin/test_monitor.sh"
docker compose logs monitor-lambda-emulator
Expand Down
Empty file modified bin/test_monitor.sh
100644 → 100755
Empty file.
2 changes: 0 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ services:
- AUTOGRAPH_KEY=19zd4w3xirb5syjgdx8atq6g91m03bdsmzjifs2oddivswlu9qs
# set a non-empty value to use the lambda handler
- LAMBDA_TASK_ROOT=/usr/local/bin/
- AUTOGRAPH_ROOT_HASH
ports:
- "9000:8080"
links:
Expand All @@ -103,7 +102,6 @@ services:
- AUTOGRAPH_KEY=19zd4w3xirb5syjgdx8atq6g91m03bdsmzjifs2oddivswlu9qs
# set a non-empty value to use the lambda handler
- LAMBDA_TASK_ROOT=/usr/local/bin/
- AUTOGRAPH_ROOT_HASH
ports:
- "9001:8080"
links:
Expand Down
39 changes: 39 additions & 0 deletions docs/endpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -380,3 +380,42 @@ Authorization: Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", ext="som
"webextensions-rsa-with-recommendation"
]
```

## /config/:keyid

### Request

Get the sanitized configuration of a signer. For example:

```bash
GET /config/dummyrsa
Host: autograph.example.net
Authorization: Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", ext="some-app-ext-data", mac="6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE="
```

### Response

400 Bad Request when the request includes a non-empty body
401 Unauthorized when the Authorization header is missing or HAWK authorization fails
404 Not Found when the keyid does not exist, or an authorization does not have permission to access the signer.
405 Method Not Allowed when the request method is not GET
200 OK when the authorization is valid and path and signer ID is found. Example response body with Content-Type application/json:

```json
{
"id": "dummyrsa",
"type": "genericrsa",
"mode": "pss",
"publickey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtEM/Vdfd4Vl9wmeVdCYuWYnQl0Zc9RW5hLE4hFA+c277qanE8XCK+ap/c5so87XngLLfacB3zZhGxIOut/4SlEBOAUmVNCfnTO+YkRk3A8OyJ4XNqdn+/ov78ZbssGf+0zws2BcwZYwhtuTvro3yi62FQ7T1TpT5VjljH7sHW/iZnS/RKiY4DwqAN799gkB+Gwovtroabh2w5OX0P+PYyUbJLFQeo5uiAQ8cAXTlHqCkj11GYgU4ttVDuFGotKRyaRn1F+yKxE4LQcAULx7s0KzvS35mNU+MoywLWjy9a4TcjK0nq+BjspKX4UkNwVstvH18hQWun7E+dxTi59cRmwIDAQAB",
"hash": "sha256"
}
```

The returned configuration should be a subset of the internal configuration with the following differences:
- Public values, such as the `id`, `publickey` and `certificate` are copied verbatim.
- Private keys are hashed, and return only the SHA256 checksum of the secret value.
- The `certificate`, if present is parsed and the following additional fields are added:
+ `cert_sha1`: Contains the SHA1 fingerprint of the DER certificate.
+ `cert_sha256`: Contains the SHA256 fingerprint of the DER certificate.
+ `cert_start`: Contains the certificate `NotBefore` time in RFC 3339 format.
+ `cert_end`: Contains the certificate `NotAfter` time in RFC 3339 format.
53 changes: 53 additions & 0 deletions handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,3 +495,56 @@ func (a *autographer) handleGetAuthKeyIDs(w http.ResponseWriter, r *http.Request
w.WriteHeader(http.StatusOK)
w.Write(signerIDsJSON)
}

// handleGetConfig returns the public signer configuration (keyID param for the API)
func (a *autographer) handleGetConfig(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
httpError(w, r, http.StatusMethodNotAllowed, "%s method not allowed; endpoint accepts GET only", r.Method)
return
}
if r.Body != nil {
body, err := io.ReadAll(r.Body)
if err != nil {
httpError(w, r, http.StatusBadRequest, "failed to read request body: %s", err)
return
}
if len(body) > 0 {
httpError(w, r, http.StatusBadRequest, "endpoint received unexpected request body")
return
}
}

pathKeyID, ok := mux.Vars(r)["keyid"]
if !ok {
httpError(w, r, http.StatusInternalServerError, "route is improperly configured")
return
}
if !signer.IDFormatRegexp.MatchString(pathKeyID) {
httpError(w, r, http.StatusBadRequest, "keyid in URL path '%s' is invalid, it must match %s", pathKeyID, signer.IDFormat)
return
}
_, headerAuthID, err := a.authorizeHeader(r)
if err != nil {
httpError(w, r, http.StatusUnauthorized, "authorization verification failed: %v", err)
return
}

requestedSigner, err := a.authBackend.getSignerForUser(headerAuthID, pathKeyID)
if err != nil {
log.Infof("user authorization failed: %s", err)
httpError(w, r, http.StatusNotFound, "keyid %s was not found for user %s", pathKeyID, headerAuthID)
return
}

requestedConfig := requestedSigner.Config()
signerConfigJSON, err := json.Marshal(requestedConfig.Sanitize())
if err != nil {
log.Errorf("handleGetConfig failed to marshal JSON with error: %s", err)
httpError(w, r, http.StatusInternalServerError, "error marshaling response JSON")
return
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(signerConfigJSON)
}
Loading

0 comments on commit 8ad7565

Please sign in to comment.