-
Notifications
You must be signed in to change notification settings - Fork 86
Secure Abacus
Abacus follows the Cloud Foundry user authentication and authorization model. It uses HTTPS to provide secure communication and then OAuth to authorize. Service providers need to acquire tokens in order to send usage documents or to retrieve reports. There are two kinds of tokens: resource token and a service token. Resource providers need to submit an OAuth token with the usage data for any resource.
With regards to the processing of submitted usage, the following rules apply:
- Resource Providers can submit usage. Resource Providers can submit usage records only for their resources.
- In case of processing errors, only an authorized user can reprocess usage that have not been processed yet.
- Only authorized users can view usage reports. Authorized users can view only their usage reports. By using the system scopes, the Abacus applications can also view all usages.
Abacus uses HTTPS as a way to provide secure communication between:
- Resource Providers and Abacus
- Usage Consumer and Abacus
- internally, between its pipeline steps/components
- Follow Cloud Foundry user authentication and authorization model.
- Do not store user credentials and their roles locally.
- Do not ask for user credentials in order to perform the authorization.
Abacus follows the Cloud Foundry user authentication and authorization model. It uses HTTPS to provide secure communication and then OAuth to authorize. Service providers need to generate tokens in order to send usage documents or to retrieve reports. There are two kinds of tokens: resource token and a service token. Resource providers need to submit an OAuth token with the usage data for any resource. All landscapes are secured by OAuth. Abacus uses OAuth to authorize:
- Resource Providers to submit usage to the pipeline
- Users to view usage reports
- Internal pipeline processing steps
OAuth uses access tokens. Abacus works with two types of access tokens: resource and system tokens.
A Resource Provider is expected to present an OAuth token with the usage it submits for a (service or runtime) resource.
The OAuth token should include:
- a user id uniquely identifying that Resource Provider
- an OAuth scope named in the following manner
abacus.usage.<resource_id>.write
A user that wants to get a report for a resource should present a token that has scope named abacus.usage.<resource_id>.read
.
For example, the linux-container
resource provider should have scope abacus.usage.linux-container.write
to submit usage. In order to get a report a user should present а token with the abacus.usage.linux-container.read
scope to the Abacus reporting component.
The Resource Providers can:
- Write data in the pipeline via the event collector (providing token with the
abacus.usage.<resource_id>.write
scope) - Read data from the pipeline via the reporting (token with
abacus.usage.<resource_id>.read
)
Additionally, the resource usage document must contain resource_id
, as specified in the API doc.
After the submission Abacus 'takes custody' of the submitted data for processing. The Abacus pipeline uses its own "system" token to authenticate and authorize the pipeline steps.
The system token has scopes abacus.usage.read
and abacus.usage.write
.
This system token is used to protect the pipeline from unauthorized (erroneous or even malicious) requests, made directly at steps of the pipeline, other than the collector and reporting.
In order to read Abacus's health and hystrix stream, basic authentication is required. Abacus uses the provided basic authentication to obtain a bearer token and validates if the user has the abacus.system.read
permission.
Abacus can work with Cloud Foundry's UAA or other token issuers, complying with JWT and JWS specifications.
The JWT specification only requires for the HMAC SHA-256 algorithm to be implemented to sign the message or token. The other algorithms are either recommended or optional. The JWS specification assumes a shared key is used - same key to sign and validate the signature.
Token issuers can support both symmetric & asymmetric cryptographic algorithms for token signature and verification. UAA supports both.
Symmetric encryption is usually used when all parties are trusted (and can therefore be trusted with the key). In environments where not all parties are trusted, asymmetric encryption should be used.
Abacus requires a specific API to communicate with the Token issuer. This API is implemented in the example Authorization Server distributed with Abacus.
Tokens are provided by requesting a UAA client (for example abacus-<resource-id>
) with:
- scopes:
abacus.usage.<resource-id>.write,abacus.usage.<resource-id>.read
- authorities:
abacus.usage.<resource-id>.write,abacus.usage.<resource-id>.read
If Abacus is configured to use CF UAA, the following uaac
command can be used for testing::
uaac client add abacus-<resource-id> --name abacus-<resource-id> --authorized_grant_types client_credentials --authorities abacus.usage.<resource-id>.write,abacus.usage.<resource-id>.read --scope abacus.usage.<resource-id>.write,abacus.usage.<resource-id>.read --secret <secret>
Note: Please make sure you have replaced the resource-id and secret above with the real ID and secret you want to use.
Example:
In order to obtain an app-usage token you need to use:
- client ID
abacus-linux-container
- secret, defined in the Abacus configuration
- required scopes for your resource id (
abacus.usage.<resource id>.read
andabacus.usage.<resource id>.write
)
You can do that with GET request:
curl --user <client id>:<client secret> -s "https://uaa.cf.<hostname>/oauth/token?grant_type=client_credentials&scope=abacus.usage.<resource id>.write%20abacus.usage.<resource id>.read"
The access tokens that are passed in the header serve as both a proof of authentication and authorization (via the scopes).
The validation of the request includes checks for:
- presence of the bearer token
- issuer, signature, expiry, and scope
UAA exposes an endpoint called check_token
, but its use increases the traffic to UAA, which makes its use in loaded environments undesired.
On the other side, the bearer token generated by UAA is a self validating JWT token. Therefore to reduce the load on UAA and increase the processing speed of the pipeline, Abacus validates the token using jsonwebtoken library, with a configurable algorithm and secret. The secret can be a password string or a public key in case of asymmetric encryption.
To run Abacus in secure mode (HTTPS + OAuth tokens), you should modify the manifest.yml
files of the Abacus application.
The set of properties that has to be changed contains:
- SECURED -
true
/false
- Usetrue
to enable the security checks - AUTH_SERVER - Authorization Server URL, used to get access token endpoint in the format of
https://hostname:port
or justhttps://hostname
. - CLIENT_ID - Client identifier, registered with the specified authorization server.
- CLIENT_SECRET - Client secret, used to authenticate the client identifier with the authorization server.
- JWTKEY - Key used to sign the JWT-JWS
- JWTALGO - Cryptographic algorithm, used to sign and secure JWT-JWS
You can use CF UAA or an Abacus authorization server as an OAuth token provider. We provide a sample implementation of such a server - the Abacus authorization plugin.
Use the following configuration:
SECURED: true
AUTH_SERVER: abacus-authserver-plugin
CLIENT_ID: abacus
CLIENT_SECRET: secret
JWTKEY: encode
JWTALGO: HS256
Note: The Authorization Server is an example implementation, defining the API Abacus uses. You should use it only for testing/development as it is not intended for productive usage.
Check your UAA configuration or CF deploy manifest on how the JSON Web Token (JWT) is signed. Search for:
- JWT algorithm
- public key (or secret)
The Abacus configuration snippet for UAA should look like this:
SECURED: true
AUTH_SERVER: https://api.<CF domain>:443
CLIENT_ID: abacus
CLIENT_SECRET: secret
JWTKEY: |+
-----BEGIN PUBLIC KEY-----
... <UAA public key> ...
-----END PUBLIC KEY-----
JWTALGO: RS256
The Abacus pipeline consists of several micro-services that run as CF applications. The applications communicate using the externally accessible endpoints, thus no special security settings are needed by default.
In order to pull monitoring data from all micro-service instances, we need to be able to access a specific instance address. Cloud Foundry hides the instances behind a router, so this is not easily achievable. Cloud Foundry's router provides a single hostname for all instances of the same application. For example, it cannot provide access to the 4th instance of an application, and instead it loadbalances between all of them.
To solve this problem we can use two approaches:
- Netflix's Eureka
- Cloud Foundry routing
Netflix's Eureka is a service discovery. All Abacus applications are registered in Eureka with their IP and port. This plays nice with the rest of the Netflix products, such as Turbine, that contacts Eureka to get a list of addresses and uses them to aggregate metrics streams. The needed configuration is described in the monitorig doc.
To enable monitoring with Eureka and Turbine, an Abacus Integrator needs to run Abacus applications into a special security group. This group shall allow direct (app-to-app) communication between Turbine and Abacus applications, so that Turbine can access each micro-service instance and fetch data for the individual app instance state. This app-to-app communication is considered a security risk. For development and testing on BOSH-Lite, you can use the all access security group. However, in production this is an additional attack vector, since if an attacker gains access to the Abacus pipeline, it can easily reach to all other CF applications.
To work around the security and routing issues, you can scale Abacus micro-services as applications instead of app instances. We can push a micro-service as a set of applications. For example app-0
, app-1
, and app-2
can form a micro-service app
with 3 instances. Check the Scale Abacus for more information. We can still use Eureka to know which application belongs to which micro-service and to get hold of the set of endpoints that form the micro-service.
The Abacus pipeline has 2 services that are used as user/agent-facing endpoints. We want these to be accessible to users without forcing them to manually load-balance through several URLs, or granting them access to Eureka and thus binding them to our internal service discovery mechanism. These are the abacus-usage-collector
that receives the usage reports from Resource Providers and abacus-usage-reporting
that serves as an endpoint that generates usage reports.
Both of these micro-services can scale via different applications or by increasing the number of instances. We need to provide a single URL for easy access to the Resource providers and Usage consumers. To do this, we should map each set of applications to a new Cloud Foundry route using cf map-route
. We can use:
-
abacus-usage-reporting
for all usage-reporting- apps -
abacus-usage-collector
for all usage-collecotr- apps
In this way the providers and reporting consumers have to keep track of just one URL. The Cloud Foundry router handles the load-balancing between the 2 sets of applications.
<< Scale Abacus | User-Specific Token >> |
---|
ABOUT | RESOURCE PROVIDER | ABACUS INTEGRATOR
*Abacus icon made by Freepik from www.flaticon.com