Policy Enforcement Point for Fiware-based applications for Telefonica IoT Platform.
50K+
The Policy Enforcement Point (PEP) is a proxy meant to secure independent FiWare components, by intercepting every request sent to the component, validating it against the Access Control component. This validation is based in several pieces of data:
x-auth-token header.fiware-service header and identifies the protected component.fiware-servicepath header and identifies further divisions of the service.Communication with the Access Control is based on the XACML protocol.
Along this document, the term IDM (Identity Manager) will be used, as a general term to refer to the server providing user and role creation and authentication. The currently supported IDM is Keystone; a Keyrock IDM option is provided as well, but it may be deprecated in the near future.
Three other documents provide further information about the PEP Proxy:
The PEP Proxy is standard Node.js app and doesn't require more dependencies than the Node.js interpreter and the NPM package utility.
Just checkout this directory and install the Node.js dependencies using:
npm install --production
The proxy should be then ready to be configured and used.
There are automatic builds of the development version of the Steelskin PEP Proxy published in Docker hub. In order to install using the docker version, just execute the following:
docker run -p 11211:11211 -p 1026:1026 -e LOG_LEVEL=DEBUG -e AUTHENTICATION_HOST=<Keystone-host> -e ACCESS_HOST=<Access-control-host> -e TARGET_HOST=<Orion-host> telefonicaiot/fiware-pep-steelskin
This command will create a docker container with a PEP Steelskin running, using for token validation and authentication, for access control and redirecting allowed requests to . Remember there are more environment variables you can use to tailor Steelskin configuration to suit your needs; you can find the complete list in the configuration section below.
Take note that this command expose two ports: the 1026 port for component requests and the administration port, 11211.
There is also the possibility to build your own local Docker image of the PEP component.
To do it, follow the next steps once you have installed Docker in your machine:
sudo docker build -f Dockerfile .
sudo docker build --build-arg NODEJS_VERSION=0.10.46 -f Dockerfile .
The PEP within the Docker image can be run encapsulated within the pm2 Process
Manager by adding the PM2_ENABLED environment variable.
docker run --name pep -e PM2_ENABLED=true -d fiware/fiware-pep-steelskin
Use of pm2 is disabled by default. It is unnecessary and counterproductive to add an additional process manager if your dockerized environment is already configured to restart Node.js processes whenever they exit (e.g. when using Kubernetes)
The PEP within the Docker image can be run with node inspection by adding the INSPECT_ENABLED environment variable.
docker run --name pep -e INSPECT_ENABLED=true -d fiware/fiware-pep-steelskin
Use of node inspection is disabled by default.
In order to undeploy the proxy, if it was installed directly from the GIT repositories, just kill the process and remove the directory.
Assuming the PEP Proxy is deployed directly from the source code, it won't add itself as a service, and the running ports should be configured manually. This configuration will involve two steps:
nohup bin/pep-proxy.js &> pep-proxy.log&
The proxy service is disabled once its installed. In order to enable it, use the following command:
service pepProxy start
Independently of how the service is installed, the log files will need an external rotation (e.g.: the logrotate command) to avoid disk full problems.
If the PEP Proxy is not started as a service, it can be started executing the following command from the project root:
bin/pep-proxy.js
Once the PEP Proxy is working, it can be used to enforce both authentication and authorization over the protected component (e.g. Orion Context Broker). In order to enforce both actions, the PEP Proxy has to be connected to an Identity Manager server and an Access Manager server. Next sections will show some examples of both processes.
Note that, in order for a request to be authorized through the Access Control, it's mandatory that it contains all of the following headers:
This headers are used by the IDM and Access Control systems to make the decisions about the request, so if any of them are missing, the request will not progress any further, and will be rejected with a 400 HTTP error code. For a detailed explanation of the returned errors, please refer to the API Error codes.
The proxy can also work in an authentication-only mode (using the config.access.disable flag), in which case the fiware-service and fiware-servicepath headers can be marked as optional, or checked anyway for validity (controlled by the config.authentication.checkHeaders flag). Header checking cannot be disabled for PEP Proxies performing authorization processes.
The authentication process is based on OAuth v2 tokens. The PEP Proxy expects all the requests to have a header x-auth-token containing a valid access token from the IDM. All the requests without this requirement are rejected with a 401 error.
PEP Proxy currently supports two possible authentication authorities: Keyrock IdM and Openstack Keystone. The following sections show how to retrieve a token with each of this authentication technologies. The module can be configured using the config.authentication.module option.
In order to get an access token to send with the request, a user can send a request to the IDM, with its user and password (here shown as a curl request):
curl -i --user <serverUser>:<serverPassword> -X POST -H "Content-Type: application/x-www-form-urlencoded" https://<idmHostName>/oauth2/token -d 'grant_type=password&username=<theUserName>&password=<theUserPassword>'
If the user and password are correct, the response will be like the following:
{
"access_token":"O-OqiBR1AbZk7qfyidF3AwMeBY253xYEpUdkv",
"refresh_token":"Ny0OwE19230QfftxXYGwwgOLafa5v2xnI5t6HWdQ",
"token_type":"bearer",
"expires_in":2591999
}
The access_token field contains the required token.
The must be used also to assign roles to each user. For details about role creation and assign, check the IDM API.
In order to get its access token, a user can send the following request to Keystone:
curl http://localhost:5000/v3/auth/tokens \
-s \
-i \
-H "Content-Type: application/json" \
-d '
{
"auth": {
"identity": {
"methods": [
"password"
],
"password": {
"user": {
"domain": {
"name": "SmartCity"
},
"name": "alice",
"password": "password"
}
}
}
}
}'
The token can be found in the X-Subject-Token header of the response:
X-Subject-Token: MIIC3AYJKoZIhvcNAQcCoIICzTCCAskCAQExCTAHBgUrDgMCGjCCATIGCSqGSIb3DQEHAaCCASMEggEfeyJ0b2tlbiI6IHsiaXNzdWVkX2F0IjogIjIwMTQtMTAtMTBUMTA6NTA6NDkuNTMyNTQyWiIsICJleHRyYXMiOiB7fSwgIm1ldGhvZHMiOiBbInBhc3N3b3JkIl0sICJleHBpcmVzX2F0IjogIjIwMTQtMTAtMTBUMTE6NTA6NDkuNTMyNDkxWiIsICJ1c2VyIjogeyJkb21haW4iOiB7ImlkIjogImY3YTViOGUzMDNlYzQzZThhOTEyZmUyNmZhNzlkYzAyIiwgIm5hbWUiOiAiU21hcnRWYWxlbmNpYSJ9LCAiaWQiOiAiNWU4MTdjNWUwZDYyNGVlNjhkZmI3YTcyZDBkMzFjZTQiLCAibmFtZSI6ICJhbGljZSJ9fX0xggGBMIIBfQIBATBcMFcxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVVbnNldDEOMAwGA1UEBwwFVW5zZXQxDjAMBgNVBAoMBVVuc2V0MRgwFgYDVQQDDA93d3cuZXhhbXBsZS5jb20CAQEwBwYFKw4DAhowDQYJKoZIhvcNAQEBBQAEggEAKRGV3uu8fiS7UNm47KhltSjlY1e7KnedUcD-mdwz6Asbo7X9hbtljy1ml9gGcuMf6vX4tycx4goRyMARPS7YKROd0evZtnYArIyx0IrmwDaqodwp8BxBCxFgHRZtCwzHvZFEaUcClydQq7HJvBfTgTwH4v1aJkMyK8wLMP-CYyiZSfCIWPVnoB9I3P56jeKHkmcryYLgT2I-AwDBj1zd9HPzUjyQuNj5rCMkJjvz-A9-hef6AMMZuYPMIYdkei+deq86O1qFuo7PpO2SA7QWkqjcsKs9v+myvHhLrBre9GLP2hP1rc4D67lSL2XB1UY20mc6FNIVIErxT0DOSXltXQ==
Vary: X-Auth-Token
Content-Type: application/json
Content-Length: 287
Date: Fri, 10 Oct 2014 10:50:49 GMT
{
"token": {
"issued_at": "2014-10-10T10:50:49.532542Z",
"extras": {},
"methods": [
"password"
],
"expires_at": "2014-10-10T11:50:49.532491Z",
"user": {
"domain": {
"id": "f7a5b8e303ec43e8a912fe26fa79dc02",
"name": "SmartValencia"
},
"id": "5e817c5e0d624ee68dfb7a72d0d31ce4",
"name": "alice"
}
}
}
For details on user and role creation, check the Keystone API.
Once the user is authenticated, the PEP Proxy will ask the Access Control for its permissions. In order for the request to be accepted, at least one rule has to match the request information and the user roles.
Rules are defined in XACML. The particular rules will depend on each case and are left to the authorization designer. The following document shows a typical rule explained for the use case of a Context Broker:
<Policy xsi:schemaLocation="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17
http://docs.oasis-open.org/xacml/3.0/xacml-core-v3-schema-wd-17.xsd"
PolicyId="policy03"
RuleCombiningAlgId="urn:oasis:names:tc:xacml:3.0:rule-combining-algorithm:deny-unless-permit"
Version="1.0" xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Target>
<AnyOf>
<AllOf>
<Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-regexp-match">
<AttributeValue
DataType="http://www.w3.org/2001/XMLSchema#string"
>frn:contextbroker:551:833:.*</AttributeValue>
<AttributeDesignator
AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id"
DataType="http://www.w3.org/2001/XMLSchema#string"
MustBePresent="true"
Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource" />
</Match>
</AllOf>
</AnyOf>
</Target>
<Rule RuleId="policy03rule01" Effect="Permit">
<Condition>
<Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
<Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-one-and-only">
<AttributeDesignator
AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id"
DataType="http://www.w3.org/2001/XMLSchema#string"
MustBePresent="true"
Category="urn:oasis:names:tc:xacml:3.0:attribute-category:action" />
</Apply>
<AttributeValue
DataType="http://www.w3.org/2001/XMLSchema#string"
>read</AttributeValue>
</Apply>
</Condition>
</Rule>
</Policy>
All the rules are associated to a service ID (the value of the fiware-service header) and a subservice. When the request arrives to the Access Control, the later will retrieve all the permissions for the user roles, each one represented by a XACML policy. All the policies are applied then in order to find any that would let the request be executed.
In the example, the policy states the following: "if the resource has the prefix frn:contextbroker:551:833: and the action read the request would be allowed". This policy will allow read access over all the resources in subservice 833 of the service 551 to the roles that have it assigned. The meaning of the term resource will depend on the component which is being protected by the particular access rules. E.g.: for Orion Context Broker, the resources will be the entities of the CB; for each entity, the Policy Enforcement Point of the CB will generate a FRN, composed of the aforementioned prefix plus the identifier of the entity itself.
Another example could be this the following:
<Policy xsi:schemaLocation="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17
http://docs.oasis-open.org/xacml/3.0/xacml-core-v3-schema-wd-17.xsd"
PolicyId="policy02"
RuleCombiningAlgId="urn:oasis:names:tc:xacml:3.0:rule-combining-algorithm:deny-unless-permit"
Version="1.0" xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Target>
<AnyOf>
<AllOf>
<Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
<AttributeValue
DataType="http://www.w3.org/2001/XMLSchema#string">admin</AttributeValue>
<AttributeDesignator
AttributeId="urn:oasis:names:tc:xacml:1.0:subject:subject-id"
DataType="http://www.w3.org/2001/XMLSchema#string"
MustBePresent="false"
Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject" />
</Match>
<Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-regexp-match">
<AttributeValue
DataType="http://www.w3.org/2001/XMLSchema#string">frn:contextbroker:551:833:.*</AttributeValue>
<AttributeDesignator
AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id"
DataType="http://www.w3.org/2001/XMLSchema#string"
MustBePresent="true"
Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource" />
</Match>
</AllOf>
</AnyOf>
</Target>
<Rule RuleId="policy02rule01" Effect="Permit">
<Condition>
<Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
<Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-one-and-only">
<AttributeDesignator
AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id"
DataType="http://www.w3.org/2001/XMLSchema#string"
MustBePresent="true"
Category="urn:oasis:names:tc:xacml:3.0:attribute-category:action" />
</Apply>
<AttributeValue
DataType="http://www.w3.org/2001/XMLSchema#string">write</AttributeValue>
</Apply>
</Condition>
</Rule>
</Policy>
In this example, only those users with subjectId (user's role) "admin" may write on resources of tenant 511 and subservice 833.
Any number of policies can be included in the Access Control for each pair (tenant, subject). If any of the policies can be applied to the request and Permit the request, then the global result is a Permit. If none of the policies can be applied (no target exist for the tenant, subservice and subject of the request) the result will be NotApplicable. If there are policies that can be applied but all of them deny the access, the result will be a Deny.
To start the service, use either the service command: service pepProxy start
Or just the launch script:
/etc/init.d/pepProxy start
For testing purposes it might be interesting to launch the process directly without the service. That can be done executing the following command from the project root directory:
./bin/pepProxy
Take into account that when the process is executed manually the system configuration for the script (in /etc/sysconfig/pepProxy) is not loaded and the default configuration (in /opt/pepProxy/config.js) is used.
To stop the service, use either the service command:
service pepProxy stop
Or just the launch script:
/etc/init.d/pepProxy stop
The status of the process can be retrieved using the service command:
service pepProxy status
It also can be checked with ps, using a filter with the command name:
ps -ef | grep "bin/pepProxy"
In both cases a result of 0 (echoing $?) indicates the process is supposed to be running, and an error otherwise.
The following command:
netstat -ntpl | grep 1026
can be used to check the process is listening in the appropriate port (provided the port is the standard 1026). The result should resemble this line:
tcp 0 0 0.0.0.0:1026 0.0.0.0:* LISTEN 12179/node
The PEP Proxy provides an Administration port that can be used to check whether the proxy is up and listening or down.
The administration API consists in a single /version path, that returns useful information from the proxy (currently just the listening port and version).
Example of output:
{
"version": "0.4.1-next",
"port": 1026
}
All the configuration of the proxy is stored in the config.js file in the root of the project folder. The values set inside config.js operate as the default values for all the important pieces of configuration data, so it is important none of them are removed (you can change them to suit your needs, as long as they have a valid value).
Another way of configuring the component is through the use of environment variables, although less configuration options are exposed with this mechanism.
In order to have the proxy running, there are several basic pieces of information to fill:
config.resource.proxy: The information of the server proxy itself. Two ports must be configured for the proxy: port indicates in which port is the proxy listening for requests; adminPort indicates the administration port. E.g.:{
port: 1026,
adminPort: 11211
}
config.resource.original: The address and port of the proxied server. E.g.:{
host: 'localhost',
port: 10026
},
config.access: connection information to the selected Access Control PDP API. Includes a disable flag, to allow the proxy to work in authentication-only mode and a flag to activate accounting access in file. E.g.:{
disable: false,
protocol: 'http',
host: 'localhost',
port: 7070,
path: '/pdp/v3',
account: false,
accountFile: '/tmp/pepAccount.log',
accountMode: 'all'
}
config.componentName: name of the component that will be used to compose the FRN that will identify the resource to be accessed. E.g.: orion.config.resourceNamePrefix: string prefix that will be used to compose the FRN that will identify the resource to be accessed. E.g.: fiware:.config.bypass: used to activate the administration bypass in the proxy. Valid values are true or false.config.bypassRoleId: ID of the role that will be considered to have administrative rights over the proxy (so being transparently proxied without validation). Valid values are Role UUIDs. E.g.: db50362d5f264c8292bebdb5c5783741.config.dieOnRedirectError: this flags changes the behavior of the PEP Proxy when an error is received when redirecting a request. If the flag is true, the PEP Proxy process is shut down immediately; if it is false, the behavior is the usual: generate a 501 Code error.config.bodyLimit: Controls the maximum request body size allowed, in bytes. Default is 1 Mbconfig.localPDP: Use local implementation for validate PDP (Policy Decision Point) or not. This validation is done by the logic in pdp.js file (out of scope of this documentation). Default is falseconfig.authentication.checkHeaders: when the proxy is working with the access control disabled (just user authentication), indicates whether the fiware-service and fiware-servicepath headers should be checked for existance and validity (checking: the headers exist, thy are not empty and the user is really part of the service and subservice mentioned in the header). This option is ignored when authorization is enabled, and considered to be true (as the headers constitute a mandatory part of the authorization process). Default value is true.config.authentication.module: indicates what type of authentication server should be used: keystone or idm. The currently supported one (and default) is keystone.config.authentication.username: username of the PEP proxy in the IDM.config.authentication.password: password of the PEP proxy in the IDM.config.authentication.domainName: (only meaningful for Keystone) name of the administration domain the PEP proxy user belongs to.config.authentication.retries: as the authentication is based in the use of tokens that can expire, the operations against Keystone are meant to retry with a fresh token. This configuration value indicates how many retries the PEP should perform in case the communication against Keystone fails. The value 0 means the default will be used (default value is 3). The value -1 implies that it should be retried forever.cacheTTLs: theContent type
Image
Digest
sha256:7645270f0…
Size
110.1 MB
Last updated
6 days ago
docker pull telefonicaiot/fiware-pep-steelskin