IT security takes center stage across enterprises as we see in the news how various organizations struggle in the aftermath of various attacks. Part of this is to secure essential assets controlling our infrastructure like the vCenter Server. For many this involves introspecting inbound API traffic to log and veto it’s content before reaching vCenter.
vCenter has grown in complexity over the years and now offers several distinct API technologies. An API introspection system needs to account for all of those technologies. The 2 most significant API types include:
1. the Web Services API using the SOAP protocol
2. the vSphere Automation API using REST design principles.
If you look closer you would notice that software build on top of the vSphere Automation SDK for Java and Python uses a different protocol. This protocol utilizes JSON and is distinctly different from the REST API despite exposing the same functionality. In this article we describe this protocol inspired from the JSON-RPC Specification and used by the vSphere Automation SDKs.
Overview
vCenter Server product includes a new style APIs since its 6.0 release. the new APIs use the REST architectural principles. These APIs include functionalities such as content library, tagging, appliance management, upgrade, security among many others. Refer to vSphere Automation API documentation for complete list. These new APIs are available on two wire protocols:
- REST inspired protocol exposed on HTTP paths under
/rest
and/api
on the vCenter Server appliance as documented in the vSphere Automation API documentation. - The vSphere Automation SDK uses a JSON-RPC inspired protocol. This protocol uses HTTP POST requests on /api on the default HTTPS port 443.
Both protocols expose the same semantic APIs using the same authentication mechanisms over HTTP and both use JSON. The JSON-RPC inspired protocol is easier to develop and deliver SDKs with. The REST inspired protocol allows developers to access the API without VMware SDKs. The REST inspired protocol is thus suitable for small scale integrations and use from languages for which VMware does not provide an official SDK.
Both protocols are used by various VMware and third-party products. It is required that both are accessible on a vCenter Server system. Notably the vSphere UI makes use of both protocols and might be adversely impacted if either protocol is blocked. Other VMware products using the JSON-RPC inspired protocol include but not limited to are VCF, vRA, vCenter cloud gateway, PowerCLI etc.
The JSON-RPC inspired protocol builds upon industry standard technologies and specializes their application to allow for inter-process communication of machine-readable data. There are two key base technologies – (1) JSON-RPC and JSON used for serializing data, (2) HTTP used for communication.
The typical pattern of communication in the JSON-RPC inspired protocol is request-response – same as the HTTP protocol.
Example JSON RPC Request:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
POST /api content-type: application/json accept: application/json vapi-service: com.vmware.vcenter.VM vapi-operation: create vapi-ctx-opid: 568c-a7b3f vmware-api-session-id: GW-1234 { "id": "5", "jsonrpc": "2.0", "method": "invoke", "params": { "serviceId": "com.vmware.vcenter.VM", "operationId": "create", "ctx": { "appCtx": { "opId": "568c-a7b3f" }, "securityCtx": { "schemeId": "com.vmware.vapi.std.security.session_id", "sessionId": "GW-1234" } }, "input": {"STRUCTURE": {"test":{}} } } } |
Example JSON RPC Response:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
200 OK content-type: application-json vapi-error: com.vmware.vapi.std.errors.unauthenticated { "jsonrpc": "2.0", "id": "5", "result": { "error": { "ERROR": { "com.vmware.vapi.std.errors.unauthenticated": { "data": { "OPTIONAL": null }, "error_type": { "OPTIONAL": "UNAUTHENTICATED" }, "messages": [ { "STRUCTURE": { "com.vmware.vapi.std.localizable_message": { "args": [], "default_message": "Authentication required.", "localized": { "OPTIONAL": null }, "id": "com.vmware.vapi.endpoint.method.authentication.required", "params": { "OPTIONAL": null } } } } ], "challenge": { "OPTIONAL": "Basic realm=\"VAPI endpoint\",SIGN realm=9912139d7483f181ccce,service=\"VAPI endpoint\",sts=\"https://test.vmware.com/sts/STSService/vsphere.local\"" } } } } } } |
Common Concerns
Clients typically access the JSON-RPC inspired protocol on port 443 over HTTP protocol. This endpoint aggregates the APIs of all vCenter Server service processes. Some components additionally expose their APIs on alternative paths and ports. Most notably the appliance management API is exposed on port 5480 allowing to manage aspects critical for the API infrastructure without interruption. When vCenter Server is configured in Enhanced Linked Mode, traffic to various service endpoints can be observed between vCenter Server systems.
The vCenter Server reverse proxy supports HTTP 1.1 since inception and 2.0 starting from vCenter Server 7.0 release.
The payload of JSON-RPC inspired protocol is always encoded in UTF-8 as per JSON RFC 8259.
Request Format
The JSON-RPC inspired protocol request builds upon the HTTP protocol request and the JSON-RPC syntax. A request consists of headers and body payload. The headers provide metadata about the request to deal with communication layer concerns and aid relaying parties. The body of the request identifies the operation to be invoked, contains its parameters, authentication and metadata about the request needed to traverse non-HTTP transports as needed.
Request Body
The request body format builds upon the basic JSON-RPC syntax to cover extended set of requirements needed by VMware vCenter Server services.
One significant difference between plain JSON-RPC and vCenter Server’s flavor is operation identification. As vCenter Server APIs are significant in number, they are organized into logical interfaces called services. The JSON-RPC inspired protocol always uses “invoke” operation on the JSON-RPC layer and uses two parameters to identify the service and operation.
Thе JSON-RPC inspired protocol adds contextual data to the request necessary for inter-machine communication. This includes application contextual data (headers) and message authentication (security).
Lastly, the JSON-RPC inspired protocol renders operation parameters into one of two JSON serialization data formats.
The JSON-RPC inspired protocol request body has the following structure:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ "id": "5", "jsonrpc": "2.0", "method": "invoke", "params": { "serviceId": "com.vmware.vcenter.VM", "operationId": "create", "ctx": { "appCtx": {}, "securityCtx": {} }, "input": { ... } } } |
Member | Description |
---|---|
jsonrpc | Identifies the JSON-RPC protocol and its version. Fixed at “2.0”. |
id | Correlation identifier allows to (de)multiplex concurrent requests and responses over the same communication channel. The request and response share the same value. Clients need to assign unique values to this field. |
method | Underlying JSON-RPC method name. Always uses the value of “invoke”. |
params.serviceId | A fully qualified service (interface) name e.g. com.vmware.vcenter.VM . |
params.operationId | Name of the operation within given service (interface) e.g. create, list, get, delete etc. |
params.ctx.appCtx | Request contextual information such as tracing information, invocation identifier etc. See “Application”Context” chapter. |
params.ctx.securityCtx | Authentication data about the request. See the “Security” chapter |
params.input | The input parameters sent to the operation. Always contains a top level structure “operation-input” that has the named parameters of the operation. In the example below there is one parameter “spec” of type “test.timer.spec” sent to the operation. See “Data Format” for more details on serializing various data structures. |
Optional Request Headers
Transport level concerns that may aid intermediaries are covered by HTTP headers.
All requests must declare their serialization format using the standard “content-type” HTTP header. The only supported value as of vCenter Server 7.0.3 is “application/json”.
Clients of the JSON-RPC inspired protocol must announce the response formats they support using the HTTP “accept” header. The basic response type used for regular RPC communication is “application/json”. Content negotiation allow to use additional formats on specific endpoints.
The following headers are used to optimize performance in certain intermediaries (clients of the public are not required to provide these). These headers are recognized by some internal endpoints (not /api) after 2018. They might be observed when vCenter Server systems communicate to each other in Enhanced Link Mode (ELM).
Header | Example | Description |
---|---|---|
vapi-service | com.vmware.content.library.item | Name of the invoked service. Matches “serviceId ” from the body. |
vapi-operation | delete | Name of the invoked operation. Matches the “operationId ” from the body |
vmware-api-session-id | 50572ecd2e99d4d | The session identifier used to access as API. |
HTTP headers render the application context members. These HTTP headers have “vapi-ctx-
” prefix. See Application Context chapter.
Response Format
There are several different formats of the service responses depending on client request, server capability and result(s) of the invoked API.
An API might return with success, error or intermediate progress update.
An API might return a single response or series of responses.
Some endpoints support selection between the specialized or clean JSON format as discussed in the Data Format chapter.
Content Negotiation
HTTP content negotiation (see RFC 7231 section 5.3.2) is used to determine the format of HTTP responses. The following options are defined though /api and most other endpoints only support application/json.
Media Type | Description |
---|---|
application/json | Single response using the specialized JSON syntax. |
application/vnd.vmware.vapi.cleanjson | Single response using the clean JSON syntax. |
application/vnd.vmware.vapi.stream.json | Stream response using the specialized JSON syntax. |
application/vnd.vmware.vapi.clean.stream.json | Stream response using the clean JSON syntax. |
Single Response
The basic API response JSON has the following schema:
1 2 3 4 5 6 7 8 9 |
{ "jsonrpc": "2.0", "id": "0451a747-95e4-44cf-865d-896dd1c9167f", "result": { "progress": {}, "output": {}, "error": {} } } |
One of “progress
“, “output
” and “error
” members is set depending on the operation status. The “progress
” type is deprecated and no longer used.
Path | Description |
---|---|
josnrpc | Specifies that the document is JSON RPC document matching the “2.0” specification. Value is always “2.0” |
id | Request identifier used for correlation of requests and responses |
result | JSON-RPC result member. |
result.progress | DEPRECATED and not used. Progress information about long running requests |
result.output | Success result of an API operation. Depending on the media type can be clean or specialized JSON syntax. |
result.output | Error response of an API operation. Depending on the media type can be clean or specialized JSON syntax. |
Below is an example of success response (clean and specialized formats match for integer value)
1 2 3 4 5 6 7 |
{ "jsonrpc": "2.0", "id": "23", "result": { "output": 1 } } |
Stream Response APIs
Certain monitoring APIs require stream output. Stream responses do not state the response size upfront and instead render as delimited stream of data chunks on the wire.
JSON-RPC inspired protocol stream responses employ a format identical to the HTTP chunked transfer encoding defined in section 4.1 of RFC 7230.
A stream response is completed by the server with either error response document or empty success response document.
1 2 3 4 5 6 7 8 9 10 |
31 {"jsonrpc":"2.0","id":"23","result":{"output":4}} 31 {"jsonrpc":"2.0","id":"23","result":{"output":3}} 31 {"jsonrpc":"2.0","id":"23","result":{"output":2}} 31 {"jsonrpc":"2.0","id":"23","result":{"output":1}} 27 {"jsonrpc":"2.0","id":"23","result":{}} |
Note that, when using HTTP/1.1, two nested layers of chunked encoding will be observed in wire dumps.
Response Headers
Similar to API requests, after 2018 API responses had been enhanced to provide headers aiding intermediaries act upon the response without parsing the JSON payload.
“vapi-error
” header is returned from newer servers carrying the type of error that occurred if the response is not success.
1 2 3 4 5 6 7 8 9 10 11 |
200 OK content-type: application-json vapi-error: com.vmware.vapi.std.errors.unauthenticated { "jsonrpc": "2.0", "id": "5", "result": { "error": { "ERROR": { "com.vmware.vapi.std.errors.unauthenticated": { |
Protocol Elements
Security
The JSON-RPC inspired protocol uses HTTP and as such needs to provide authentication mechanics for every request. The authentication data is part of the JSON payload and resides in a security context JSON object (params.ctx.securityCtx
).
The security context object has authentication scheme (schemeId
) member and additional scheme specific members that carry the credentials
1 2 3 4 5 6 7 |
{ "params": { "ctx": { "securityCtx": { "schemeId": "com.vmware.vapi.std.security.session_id", "sessionId": "GW-1234" } |
In vCenter Server as of 7.0.3 uses the following authentication schemes:
com.vmware.vapi.std.security.no_authentication
Used for anonymous API calls. The is no additional data included in this case.
com.vmware.vapi.std.security.session_id
Used for calling APIs with a session token. The majority of call to vCenter API use this authentication.
- “
sessionId
” member carries the value of the session identifier. The session create API allocates session identifiers.
com.vmware.vapi.std.security.saml_hok_token
SAML Holder of Key assertion. This is typically used to create a session token with the session API. This requires that the API call is digitally signed with the private key corresponding to the SAML assertion. This is a very slow way to call APIs and the size of request is increased significantly and it is recommended to only use this mechanism for establishing sessions.
The following JSON format includes SAML Holder of Key assertion:
1 2 3 4 5 6 7 8 9 10 11 |
"schemeId': 'com.vmware.vapi.std.security.saml_hok_token", "signatureAlgorithm": "<SIGNATURE_ALGORITHM>", "signature": { "samlToken":"<TOKEN_TEXT>", "value":"<DIGEST>" } "timestamp': { "created":"<CREATE_TS>", "expires":"<EXPIRES_TS>" } } |
- “
SIGNATURE_ALGORITHM
” – algorithm used for signing the request. Typical value is “RS256” denoting digital signature with RSA and SHA256 - “
TOKEN_TEXT
” – the SAML assertion XML text escaped to fit JSON string - “
DIGEST
” – the computed signature value - “
CREATE_TS
” – timestamp when the signature is created. Has not-valid-before semantics. Signature is not valid before this moment. - “
EXPIRE_TS
” – timestamp for signature expiration. Signature is not valid after this moment.
Date and time values follow a subset of ISO 8601. Always use UTC timezone (Z). Always have millisecond precision. For example 2012-10-26T12:24:18.941Z
All timestamp comparisons use clock skew tolerance allowance.
The JSON request is first canonicalized (object members sorted alphabetically) before computing the hash. The hash is computed on the full JSON-RPC message.
com.vmware.vapi.std.security.saml_bearer_token
SAML bearer token assertion. The session API uses SAML tokens to create and user session. The security context only transports the SAML assertion. There is no digital signature on the request. The size of the requests increases significantly when it includes a SAML token. SAML tokens should only be used for establishing sessions.
- “
samlToken
” member carries the SAML assertion text.
com.vmware.vapi.std.security.user_pass
User and Password authentication. Semantically this is identical to HTTP Authorization Basic. The create session API uses this to issue a session ID.
- “
userName
” – the user name - “
password
” – the password
com.vmware.vapi.std.security.oauth
OAuth2 authentication token. This matches the semantics described in RFC 6750 chapter 2.1 Authorization Request Header Field. This is typically a JSON Web Token (JWT). In vCenter Server 7.0.3 the Issue Token API uses this method to exchange JWT for SAML assertions useful for creating an API session.
- “accessToken” – the OAuth2 token text
Application Context
The application context (params.ctx.appCtx
) is a required element of the JSON-RPC requests. The application context transfers contextual data that is not part of an API input declaration. This additional data serves cross-cutting concerns across all APIs. The application context thus resembles the HTTP headers. It is part of the JSON payload as to allow channeling of messages through other transports such as message bus.
1 2 3 4 5 6 7 |
{ "params": { "ctx": { "appCtx": { "vcenter-id": "fast-vcenter-1", "opId": "568c-a7b3f" }, |
The application context is a JSON object that only allows for string value members.
After 2018, the HTTP headers include the members of the application context to allow for faster processing by certain components of vCenter Server. HTTP Headers prefixed with “vapi-ctx-
” represent application context members. For example, a “vcenter-id
” application context member renders by SDKs as “vapi-ctx-vcenter-id
” HTTP header.
Members of the application context should use lower case names as to allow transporting them as HTTP/2 header names and case-insensitive processing.
Few well known application context members use case sensitive names. The server and SDKs code know to special handle these members. These include
Lower case HTTP header name | Case Sensitive Name used in JSON payload | Semantics |
---|---|---|
$useragent | $userAgent | Used to transfer original user-agent header value as received by an API Gateway |
actid | actId | Activation identifier used by content library to correlate tasks created in vpxd TaskManager |
opid | opId | Identifier used for tracking calls between services. |
For example, as per the above table the “opId” member of the application context maps to “vapi-ctx-opid” HTTP header.
Data Format
The JSON-RPC inspired protocol uses the basic JSON types for serialization. It offers two data formats. The first one let’s call it specialized syntax has embedded type metadata and builds upon the JSON constructs to define specialized semantics to better suite scripting (reflective) and compiled clients. The second format uses directly the JSON format as a result it minimizes payload size and increases compatibility with 3rd tooling.
The clean format is only supported on small sub set of endpoints and is not used by SDK clients.
Below are the definitions of the different data types used in the protocol and how they are serialized:
Binary
Specialized: { "BINARY": "SGVsbG8=" }
Clean JSON: "SGVsbG8="
A base64 encoded value
Boolean
Specialized: true
Clean JSON: true
JSON true or false
value
Double
Specialized: 3.14
Clean JSON: 3.14
JSON number with decimal or exponent. It needs to fit in a 64 bit float.
Error
Specialized: {"ERROR":{"com.vmware.vapi.std.errors.error":{ ... }}}
Clean JSON: { ... }
Indicates error type and identifies the declared type of error (“com.vmware.vapi.std.errors.error
” in the example). The payload is a JSON object that contains the various fields of the error type
Integer
Specialized: 42
Clean JSON: 42
JSON number without decimal or exponent. It needs to fit in a signed 64 bit integer.
List
Specialized: [42, 43]
Clean JSON: [42, 43]
JSON array representing a list of values
Map
Specialized: [{"STRUCTURE":{"map_entry":{"key":"string_key","value":"string_value"}}}]
Clean JSON: {"string_key":"string_value"}
The APIs type system supports the map type. A map is a JSON list of key and value pairs in the specialized syntax. The clean JSON syntax uses a plain JSON object for the same purpose. The supported key types include discreet primitive types such as integer and string.
Optional
Specialized: {"OPTIONAL":42}
Clean JSON: 42
In the specialized syntax this is indication that particular value is declared as optional. This indication is required around values that can be null. In the clean syntax “null” values are typically not rendered. The optional values are skipped in clean JSON unless they are member of an array and null is used to preserve indexes.
Secret
Specialized: {"SECRET":"password"}
Clean JSON: "password"
Indication that particular value contains sensitive data that software should not log in clear text.
String
Specialized: "Hello"
Clean JSON: "Hello"
String value.
Structure
Specialized: {"STRUCTURE":{"test.timer.spec":{"client_name": "client"}}}
Clean JSON: {"client_name": "client"}
Indicates structure type and identifies the declared type of structure (“test.timer.spec” in the example). The payload is a JSON object that contains the fields of the structure type.
Void
Specialized: –
Clean JSON: –
Indicates void declaration. For example, void response of an API call. Void is not rendered on the wire.
Example of Specialized syntax
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
{ "STRUCTURE": { "com.vmware.vapi.is.test.test_model_with_ids": { "id": "primary_id", "list_of_ids": [ "id_in_list" ], "list_of_strings": [ "string_in_list" ], "map_with_id_key": [ { "STRUCTURE": { "map_entry": { "key": "id_key_in_map", "value": "string_value_in_map" } } } ], "nested_model": { "STRUCTURE": { "com.vmware.vapi.is.test.nested_model": { "nested_id": "nested_model_id" } } }, "optional_id": { "OPTIONAL": "optional_id" }, "unset_optional_id": { "OPTIONAL": null }, "set_of_ids": [ "id_in_set" ], "set_of_strings": [ "string_in_set" ] } } } |
Example of Clean JSON
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
{ "id": "primary_id", "list_of_ids": [ "id_in_list" ], "list_of_strings": [ "string_in_list" ], "map_with_id_key": { "id_key_in_map": "string_value_in_map" }, "nested_model": { "nested_id": "nested_model_id" }, "optional_id": "optional_id", "set_of_ids": [ "id_in_set" ], "set_of_strings": [ "string_in_set" ] } |
Follow us on Twitter @VMware_API_team and send us any feedback.