generated from TrueCloudLab/basic
[#74] docs: Describe converters #74
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