forked from TrueCloudLab/policy-engine
[#74] docs: Describe converters
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
parent
2fa27b6557
commit
2e7518c453
1 changed files with 439 additions and 0 deletions
439
docs/policy_converters.md
Normal file
439
docs/policy_converters.md
Normal file
|
@ -0,0 +1,439 @@
|
|||
# Policy converters
|
||||
|
||||
This repository contains converters that provide opportunities to
|
||||
transform [AWS IAM policies](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies.html) to inner
|
||||
FrostFS policy format. This document describes such transformations.
|
||||
|
||||
## FrostFS
|
||||
|
||||
As it was mentioned there are converters that transform AWS IAM policies to FrostFS.
|
||||
Here common examples of AWS:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:*"
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
and FrostFS:
|
||||
|
||||
```json
|
||||
{
|
||||
"ID": "c29tZS1pZA==",
|
||||
"Rules": [
|
||||
{
|
||||
"Status": "Allow",
|
||||
"Actions": {
|
||||
"Inverted": false,
|
||||
"Names": [
|
||||
"s3:*"
|
||||
]
|
||||
},
|
||||
"Resources": {
|
||||
"Inverted": false,
|
||||
"Names": [
|
||||
"*"
|
||||
]
|
||||
},
|
||||
"Any": false,
|
||||
"Condition": null
|
||||
}
|
||||
],
|
||||
"MatchType": "DenyPriority"
|
||||
}
|
||||
```
|
||||
|
||||
policies.
|
||||
|
||||
Despite there is only one FrostFS format, we have two converters (`s3` and `native`). The reason is S3 gateway and
|
||||
Storage node have different actions and resource naming:
|
||||
|
||||
* S3 has [a lot of methods](https://docs.aws.amazon.com/AmazonS3/latest/API/API_Operations.html) and operates with
|
||||
bucket/object
|
||||
* Storage node has only 6 container and 7 object methods and operates container/object (that has different format)
|
||||
|
||||
The following sections describe each transformation more precisely ([common](#common) sections contains shared concepts)
|
||||
|
||||
### Common
|
||||
|
||||
#### Fields
|
||||
|
||||
Rough json main fields mapping:
|
||||
|
||||
| AWS policy field | FrostFS policy field | Comment |
|
||||
|------------------|----------------------|------------------------------------------------------------------|
|
||||
| `Version` | - | Not applicable |
|
||||
| `Statement` | `Rules` | |
|
||||
| `Effect` | `Status` | |
|
||||
| `Action` | `Actions.Names` | `Actions.Inverted` = false |
|
||||
| `NotAction` | `Actions.Names` | `Actions.Inverted` = true |
|
||||
| `Resource` | `Resources.Names` | `Resources.Inverted` = false |
|
||||
| `NotResource` | `Resources.Names` | `Resources.Inverted` = true |
|
||||
| `Condition` | `Condition` | `Any` = false, that means the conditions must be hold altogether |
|
||||
| `Principal` | - | Expressed via conditions (depends on s3/native converters) |
|
||||
|
||||
### Conditions
|
||||
|
||||
Each condition in FrostFS policy can add requirements to some request/resource properties
|
||||
and consists of the following fields:
|
||||
|
||||
| Field | Description |
|
||||
|----------|-------------------------------------------------------------------------------------------|
|
||||
| `Op` | Condition type operation (`StringEqual`, `NumericEqual` etc) |
|
||||
| `Object` | Property type to which condition can be applied (`Request` property, `Resource` property) |
|
||||
| `Key` | Property key |
|
||||
| `Value` | Property value |
|
||||
|
||||
Conditions operators:
|
||||
|
||||
| AWS conditions operator | FrostFS condition operator | Comment |
|
||||
|-----------------------------|-----------------------------|-------------------------------------------------------------------|
|
||||
| `StringEquals` | `StringEquals` | |
|
||||
| `StringNotEquals` | `StringNotEquals` | |
|
||||
| `StringEqualsIgnoreCase` | `StringEqualsIgnoreCase` | |
|
||||
| `StringNotEqualsIgnoreCase` | `StringNotEqualsIgnoreCase` | |
|
||||
| `StringLike` | `StringLike` | |
|
||||
| `StringNotLike` | `StringNotLike` | |
|
||||
| `NumericEquals` | `NumericEquals` | |
|
||||
| `NumericNotEquals` | `NumericNotEquals` | |
|
||||
| `NumericLessThan` | `NumericLessThan` | |
|
||||
| `NumericLessThanEquals` | `NumericLessThanEquals` | |
|
||||
| `NumericGreaterThan` | `NumericGreaterThan` | |
|
||||
| `NumericGreaterThanEquals` | `NumericGreaterThanEquals` | |
|
||||
| `DateEquals` | `StringEquals` | Date transforms to unix timestamp to be compared as string |
|
||||
| `DateNotEquals` | `StringNotEquals` | Date transforms to unix timestamp to be compared as string |
|
||||
| `DateLessThan` | `StringEqualsIgnoreCase` | Date transforms to unix timestamp to be compared as string |
|
||||
| `DateLessThanEquals` | `StringNotEqualsIgnoreCase` | Date transforms to unix timestamp to be compared as string |
|
||||
| `DateGreaterThan` | `StringLike` | Date transforms to unix timestamp to be compared as string |
|
||||
| `DateGreaterThanEquals` | `StringNotLike` | Date transforms to unix timestamp to be compared as string |
|
||||
| `Bool` | `StringEqualsIgnoreCase` | |
|
||||
| `IpAddress` | `IPAddress` | |
|
||||
| `NotIpAddress` | `NotIPAddress` | |
|
||||
| `ArnEquals` | `StringEquals` | |
|
||||
| `ArnLike` | `StringLike` | |
|
||||
| `ArnNotEquals` | `StringNotEquals` | |
|
||||
| `ArnNotLike` | `StringNotLike` | |
|
||||
| `SliceContains` | `SliceContains` | AWS spec doesn't contain such operator. This is FrostFS extension |
|
||||
|
||||
For example, AWS conditions:
|
||||
|
||||
```json
|
||||
{
|
||||
"Condition": {
|
||||
"ArnEquals": {"key16": ["val16"]},
|
||||
"ArnNotEquals": {"key18": ["val18"]},
|
||||
"ArnNotLike": {"key19": ["val19"]},
|
||||
"Bool": {"key13": ["True"]},
|
||||
"DateEquals": {"key7": ["2006-01-02T15:04:05+07:00"]},
|
||||
"DateGreaterThan": {"key11": ["2006-01-02T15:04:05-01:00"]},
|
||||
"DateGreaterThanEquals": {"key12": ["2006-01-02T15:04:05-03:00"]},
|
||||
"DateLessThan": {"key9": ["2006-01-02T15:04:05+06:00"]},
|
||||
"DateLessThanEquals": {"key10": ["2006-01-02T15:04:05+03:00"]},
|
||||
"DateNotEquals": {"key8": ["2006-01-02T15:04:05Z"]},
|
||||
"NumericEquals": {"key20": ["-20"]},
|
||||
"NumericGreaterThan": {"key24": ["-24.24"]},
|
||||
"NumericGreaterThanEquals": {"key25": ["+25.25"]},
|
||||
"NumericLessThan": {"key22": ["0"]},
|
||||
"NumericLessThanEquals": {"key23": ["23.23"]},
|
||||
"NumericNotEquals": {"key21": ["+21"]},
|
||||
"StringEquals": {"key1": ["val0"]},
|
||||
"StringEqualsIgnoreCase": {"key3": ["val3"]},
|
||||
"StringLike": {"key5": ["val5"]},
|
||||
"StringNotEquals": {"key2": ["val2"]},
|
||||
"StringNotEqualsIgnoreCase": {"key4": ["val4"]},
|
||||
"StringNotLike": {"key6": ["val6"]}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
transforms to FrostFS conditions:
|
||||
|
||||
```json
|
||||
{
|
||||
"Condition": [
|
||||
{"Op": "StringLike", "Object": "Request", "Key": "key5", "Value": "val5"},
|
||||
{"Op": "StringNotEquals", "Object": "Request", "Key": "key2", "Value": "val2"},
|
||||
{"Op": "StringGreaterThan", "Object": "Request", "Key": "key11", "Value": "1136217845"},
|
||||
{"Op": "StringGreaterThanEquals", "Object": "Request", "Key": "key12", "Value": "1136225045"},
|
||||
{"Op": "StringLessThan", "Object": "Request", "Key": "key9", "Value": "1136192645"},
|
||||
{"Op": "StringEqualsIgnoreCase", "Object": "Request", "Key": "key3", "Value": "val3"},
|
||||
{"Op": "StringEquals", "Object": "Request", "Key": "key16", "Value": "val16"},
|
||||
{"Op": "NumericLessThanEquals", "Object": "Request", "Key": "key23", "Value": "23.23"},
|
||||
{"Op": "StringNotEqualsIgnoreCase", "Object": "Request", "Key": "key4", "Value": "val4"},
|
||||
{"Op": "StringEquals", "Object": "Request", "Key": "key1", "Value": "val0"},
|
||||
{"Op": "StringLessThanEquals", "Object": "Request", "Key": "key10", "Value": "1136203445"},
|
||||
{"Op": "NumericGreaterThan", "Object": "Request", "Key": "key24", "Value": "-24.24"},
|
||||
{"Op": "NumericGreaterThanEquals", "Object": "Request", "Key": "key25", "Value": "+25.25"},
|
||||
{"Op": "NumericLessThan", "Object": "Request", "Key": "key22", "Value": "0"},
|
||||
{"Op": "StringNotEquals", "Object": "Request", "Key": "key8", "Value": "1136214245"},
|
||||
{"Op": "NumericEquals", "Object": "Request", "Key": "key20", "Value": "-20"},
|
||||
{"Op": "NumericNotEquals", "Object": "Request", "Key": "key21", "Value": "+21"},
|
||||
{"Op": "StringNotLike", "Object": "Request", "Key": "key6", "Value": "val6"},
|
||||
{"Op": "StringNotEquals", "Object": "Request", "Key": "key18", "Value": "val18"},
|
||||
{"Op": "StringNotLike", "Object": "Request", "Key": "key19", "Value": "val19"},
|
||||
{"Op": "StringEqualsIgnoreCase", "Object": "Request", "Key": "key13", "Value": "True"},
|
||||
{"Op": "StringEquals", "Object": "Request", "Key": "key7", "Value": "1136189045"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### S3
|
||||
|
||||
#### Actions
|
||||
|
||||
Each action allows some s3-gw methods, so we must transform action to specific method names
|
||||
(you can see exact mapping in table in [this file](../iam/converter_s3.go)).
|
||||
|
||||
For example the following actions:
|
||||
|
||||
```json
|
||||
{
|
||||
"Action": [
|
||||
"s3:DeleteObject",
|
||||
"iam:CreateUser"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
transforms to
|
||||
|
||||
```json
|
||||
{
|
||||
"Actions": {
|
||||
"Inverted": false,
|
||||
"Names": [
|
||||
"s3:DeleteObject",
|
||||
"s3:DeleteMultipleObjects",
|
||||
"iam:CreateUser"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
As we can see any `iam:*` action transformed as it is. But `s3:*` actions transforms according to
|
||||
[spec rules](https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazons3.html) and s3-gw
|
||||
[method names](https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw/src/commit/2ab655b909c40db6f7a4e41e07d8b99167f791bd/api/middleware/constants.go#L3-L76).
|
||||
|
||||
#### Resources
|
||||
|
||||
Resource is transformed as it is:
|
||||
|
||||
```json
|
||||
{
|
||||
"Resource": [
|
||||
"arn:aws:s3:::bucket/object"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"Resources": {
|
||||
"Inverted": false,
|
||||
"Names": [
|
||||
"arn:aws:s3:::bucket/object"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Principals
|
||||
|
||||
To check user s3-gw uses special condition request property (`Owner`), so when AWS policy contains principal field
|
||||
it transforms to rule with appropriate condition. To get correct `Owner` property value special user resolver
|
||||
(`S3Resolver` interface in [converter_s3 file](../iam/converter_s3.go)) must be provided into convert function.
|
||||
|
||||
For example such AWS json statement:
|
||||
|
||||
```json
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "*",
|
||||
"Resource": "*",
|
||||
"Principal": {
|
||||
"AWS": "arn:aws:iam::111122223333:user/JohnDoe"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
transforms to the following FrostFS rule:
|
||||
|
||||
```json
|
||||
{
|
||||
"Status": "Allow",
|
||||
"Actions": {
|
||||
"Inverted": false,
|
||||
"Names": [
|
||||
"*"
|
||||
]
|
||||
},
|
||||
"Resources": {
|
||||
"Inverted": false,
|
||||
"Names": [
|
||||
"*"
|
||||
]
|
||||
},
|
||||
"Any": false,
|
||||
"Condition": [
|
||||
{
|
||||
"Op": "StringEquals",
|
||||
"Object": "Request",
|
||||
"Key": "Owner",
|
||||
"Value": "NbUgTSFvPmsRxmGeWpuuGeJUoRoi6PErcM"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Native
|
||||
|
||||
#### Actions
|
||||
|
||||
Each action allows some frostfs methods, so we must transform action to specific method names
|
||||
(you can see exact mapping in table in [this file](../iam/converter_native.go)).
|
||||
|
||||
For example the following actions:
|
||||
|
||||
```json
|
||||
{
|
||||
"Action": [
|
||||
"s3:DeleteObject",
|
||||
"iam:CreateUser"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
transforms to
|
||||
|
||||
```json
|
||||
{
|
||||
"Actions": {
|
||||
"Inverted": false,
|
||||
"Names": [
|
||||
"PutObject",
|
||||
"HeadObject",
|
||||
"GetObject",
|
||||
"RangeObject",
|
||||
"GetContainer",
|
||||
"DeleteObject"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **Note:** Only subset of s3:* actions can be transformed (exact value you can see in mapping table mentioned before).
|
||||
> If all provided actions is not applicable converter function returns appropriate error.
|
||||
|
||||
Native methods (to which original actions are transformed) depend on which methods are invoked by appropriate s3-gw
|
||||
[method](https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw/src/commit/2ab655b909c40db6f7a4e41e07d8b99167f791bd/api/middleware/constants.go#L3-L76).
|
||||
|
||||
So in example above s3-gw during performing DeleteObject methods invokes the following methods:
|
||||
`["PutObject","HeadObject","GetObject","RangeObject","GetContainer","DeleteObject"]`
|
||||
|
||||
#### Resources
|
||||
|
||||
To transform resources the following is being performed:
|
||||
|
||||
* Bucket name is resoled to container id (by providing `NativeResolver` interface implementation to converter)
|
||||
* Object name is transformed to condition with special `FilePath` attribute
|
||||
(that present on every object that was uploaded via s3-gw)
|
||||
|
||||
For example, the following AWS policy statement:
|
||||
|
||||
```json
|
||||
{
|
||||
"Principal": "*",
|
||||
"Effect": "Allow",
|
||||
"Action": "*",
|
||||
"Resource": "arn:aws:s3:::bucket/object"
|
||||
}
|
||||
```
|
||||
|
||||
transforms to FrostFS native policy rule:
|
||||
|
||||
```json
|
||||
{
|
||||
"Status": "Allow",
|
||||
"Actions": {
|
||||
"Inverted": false,
|
||||
"Names": [
|
||||
"*"
|
||||
]
|
||||
},
|
||||
"Resources": {
|
||||
"Inverted": false,
|
||||
"Names": [
|
||||
"native:object//bucket/HFq67qbfhFEiEL7qDXqayo3F78yAvxXSXzwSa2hKM9bH/*",
|
||||
"native:container//bucket/HFq67qbfhFEiEL7qDXqayo3F78yAvxXSXzwSa2hKM9bH"
|
||||
]
|
||||
},
|
||||
"Any": false,
|
||||
"Condition": [
|
||||
{
|
||||
"Op": "StringLike",
|
||||
"Object": "Resource",
|
||||
"Key": "FilePath",
|
||||
"Value": "object"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Principals
|
||||
|
||||
To check user s3-gw uses special condition request property (`$Actor:publicKey`), so when AWS policy contains principal
|
||||
field it transforms to rule with appropriate condition. To get correct `$Actor:publicKey` property value
|
||||
special user resolver (`NativeResolver` interface in [converter_native file](../iam/converter_native.go)) must be
|
||||
provided into convert function.
|
||||
|
||||
For example such AWS json statement:
|
||||
|
||||
```json
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "*",
|
||||
"Resource": "*",
|
||||
"Principal": {
|
||||
"AWS": "arn:aws:iam::111122223333:user/JohnDoe"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
transforms to the following FrostFS rule:
|
||||
|
||||
```json
|
||||
{
|
||||
"Status": "Allow",
|
||||
"Actions": {
|
||||
"Inverted": false,
|
||||
"Names": [
|
||||
"*"
|
||||
]
|
||||
},
|
||||
"Resources": {
|
||||
"Inverted": false,
|
||||
"Names": [
|
||||
"native:object/*",
|
||||
"native:container/*"
|
||||
]
|
||||
},
|
||||
"Any": false,
|
||||
"Condition": [
|
||||
{
|
||||
"Op": "StringEquals",
|
||||
"Object": "Request",
|
||||
"Key": "$Actor:publicKey",
|
||||
"Value": "031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4a"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
Loading…
Reference in a new issue