policy-engine/docs/policy_converters.md
Denis Kirillov 2e7518c453
All checks were successful
Tests and linters / Tests (1.20) (pull_request) Successful in 55s
Tests and linters / Tests (1.21) (pull_request) Successful in 1m5s
DCO action / DCO (pull_request) Successful in 58s
Tests and linters / Staticcheck (pull_request) Successful in 1m14s
Tests and linters / Tests with -race (pull_request) Successful in 1m31s
Tests and linters / Lint (pull_request) Successful in 2m16s
[#74] docs: Describe converters
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2024-05-07 13:10:02 +03:00

16 KiB

Policy converters

This repository contains converters that provide opportunities to transform AWS IAM policies 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:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:*"
      ],
      "Resource": "*"
    }
  ]
}

and FrostFS:

{
  "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 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 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:

{
  "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:

{
  "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).

For example the following actions:

{
  "Action": [
    "s3:DeleteObject",
    "iam:CreateUser"
  ]
}

transforms to

{
  "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 and s3-gw method names.

Resources

Resource is transformed as it is:

{
  "Resource": [
    "arn:aws:s3:::bucket/object"
  ]
}
{
  "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) must be provided into convert function.

For example such AWS json statement:

{
  "Effect": "Allow",
  "Action": "*",
  "Resource": "*",
  "Principal": {
    "AWS": "arn:aws:iam::111122223333:user/JohnDoe"
  }
}

transforms to the following FrostFS rule:

{
  "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).

For example the following actions:

{
  "Action": [
    "s3:DeleteObject",
    "iam:CreateUser"
  ]
}

transforms to

{
  "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.

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:

{
  "Principal": "*",
  "Effect": "Allow",
  "Action": "*",
  "Resource": "arn:aws:s3:::bucket/object"
}

transforms to FrostFS native policy rule:

{
  "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) must be provided into convert function.

For example such AWS json statement:

{
  "Effect": "Allow",
  "Action": "*",
  "Resource": "*",
  "Principal": {
    "AWS": "arn:aws:iam::111122223333:user/JohnDoe"
  }
}

transforms to the following FrostFS rule:

{
  "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"
    }
  ]
}