# 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" } ] } ```