Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/feature-branch-only-to-main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: "Restrict Docs PR merges"

on:
pull_request:
types: [opened, reopened, edited]

jobs:
validate-refs:
name: "Validate Refs"
runs-on: ubuntu-latest
steps:
- name: Validate
run: |
# We allow merging to a branch which is a feature branch for documentation.
if [[ $GITHUB_BASE_REF == aws-docs-* || $GITHUB_BASE_REF == snowflake-docs-* ]]; then
echo "Merging to a feature branch. Allowing."
exit 0
fi

# We allow merging to main so long as it's from a feature branch for documentation.
if [[ $GITHUB_BASE_REF == main && ($GITHUB_HEAD_REF == aws-docs-* || $GITHUB_HEAD_REF == snowflake-docs-*) ]]; then
echo "Merging feature branch to main. Allowing."
exit 0
fi

# All other scenarios aren't valid for merging.
echo "Please ensure all changes are merged to a feature branch. Only feature branches can be merged into main. Rejecting."
exit 1
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ This section covers configuration options that are specific to certain AWS servi
| `EKS_START_K3D_LB_INGRESS` | `0` (default) | Whether to start the k3d load balancer and Traefik ingress controller automatically when creating an EKS cluster. Set to `1` to enable. |
| `EKS_PERSIST_CLUSTER_CONTENTS` | `0` (default) | When Persistence is enabled or when saving/loading Cloud Pods, this flag can be used to control whether the content deployed to EKS clusters will be persisted. Set to `1` to enable. |
| `EKS_K3D_CLUSTER_TOKEN` | `localstack-k3d-cluster-token` (default) | Token used to authenticate agent nodes joining a k3d-backed EKS cluster. Setting an explicit token ensures consistent node authentication across k3d versions, which is required for dynamic agent assignment. Can be overridden via the `EKS_K3D_CLUSTER_TOKEN` environment variable. |
| `EKS_K3D_VERSION` | `v5.9.0` (default) | Overrides the k3d CLI version used to manage EKS clusters. |

### ElastiCache

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ It only includes operations performed with a principal, not as root, so test set
| **Permission Boundaries** | |
| | - Roles |
| | - Users |
| **Service Control Policies (SCPs)** | |
| | - Enforced across the organization hierarchy (root, OU, account) |
| | - Enforced for cross-account access |
| | - Evaluated by the IAM Policy Simulator |

## Supported Policy Features

Expand Down
1 change: 0 additions & 1 deletion src/content/docs/aws/services/kinesisanalyticsv2.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,6 @@ awslocal kinesisanalyticsv2 untag-resource \
| 1.19.1 | yes | yes |
| 1.18.1 | yes | yes |
| 1.15.2 | yes | no |
| 1.13.1 | yes | no |

## Limitations

Expand Down
69 changes: 69 additions & 0 deletions src/content/docs/aws/services/organizations.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,75 @@ To get started, start your LocalStack instance using your preferred method:
awslocal organizations delete-organization
```

## Service Control Policy enforcement

[Service Control Policies (SCPs)](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps.html) set the maximum permissions for accounts in your organization.
When IAM enforcement is enabled, LocalStack checks SCPs together with other applicable policies.
A request goes through only if both the principal's policies, resource's policies and the SCPs covering its account allow the action on the resource.

To turn on SCP enforcement, start LocalStack with [`ENFORCE_IAM=1`](/aws/developer-tools/security-testing/iam-policy-enforcement) and enable the `SERVICE_CONTROL_POLICY` policy type on your organization root (see the [getting started](#getting-started) steps above).

LocalStack evaluates SCPs at each level of the organization hierarchy: root, organizational unit, and account.
An action must be allowed by an SCP at every level between the root and the account.
If any level lacks an `Allow`, the result is an implicit deny, and an explicit `Deny` overrides any `Allow`.

:::note
The organization's management (master) account is exempt from SCPs.
Principals in the management account are never restricted by SCPs, even with an explicit `Deny` SCP attached.
:::

### Cross-account access

LocalStack enforces SCPs for [cross-account access](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic-cross-account.html), where a principal in one account uses a resource owned by another account.

For a cross-account request, LocalStack checks the SCPs of the source account (the account making the request).
A deny in those SCPs blocks the request even when the target resource's policy grants access.

Consider a member account that lists the objects of an S3 bucket in another account of the same organization, with a bucket policy that grants the member account access:

```bash
# Run as the member (source) account
awslocal s3api list-objects-v2 --bucket cross-account-bucket
```

The default `FullAWSAccess` SCP lets the request succeed on the bucket policy.
Attach an SCP that denies `s3:ListBucket` to the member account, and the request fails:

```bash title="Output"
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: User: arn:aws:iam::111111111111:user/test is not authorized to perform: s3:ListBucket on resource: "arn:aws:s3:::cross-account-bucket" with an explicit deny in a service control policy
```

An SCP that allows only unrelated actions (for example, an `ec2:*`-only SCP) produces an implicit deny, since no SCP allows `s3:ListBucket`:

```bash title="Output"
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: User: arn:aws:iam::111111111111:user/test is not authorized to perform: s3:ListBucket on resource: "arn:aws:s3:::cross-account-bucket" because no service control policy allows the s3:ListBucket action
```

The management account stays exempt from SCPs for cross-account requests too.

### Testing SCPs with the IAM Policy Simulator

You can use the [IAM Policy Simulator](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_testing-policies.html) to check whether a principal's request would be allowed or denied without running it against your resources.
LocalStack evaluates SCPs during policy simulation, so you can validate SCP behavior with [`SimulatePrincipalPolicy`](https://docs.aws.amazon.com/IAM/latest/APIReference/API_SimulatePrincipalPolicy.html) before making live requests.

```bash
awslocal iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::111111111111:user/test \
--action-names s3:ListBucket \
--resource-arns arn:aws:s3:::cross-account-bucket
```

When SCPs affect the decision, LocalStack populates the `OrganizationsDecisionDetail` field of the [`EvaluationResult`](https://docs.aws.amazon.com/IAM/latest/APIReference/API_EvaluationResult.html), so you can see whether an SCP allowed the action.

:::note
LocalStack's Policy Simulator and its IAM enforcement engine share the same underlying evaluation logic.
As a result, the simulator reflects the real AWS IAM behavior rather than the behavior of the AWS Policy Simulator, which differs in a few ways:

- AWS ignores SCPs that contain conditions during simulation. LocalStack evaluates SCPs with conditions.
- AWS applies SCPs to the organization's management account during simulation. LocalStack does not apply SCPs to the management account, matching the real behavior of AWS Organizations.
- AWS reports an explicit `Deny` from an SCP as an implicit deny. LocalStack reports it as an explicit deny, which is the expected outcome.
:::

## API Coverage

<FeatureCoverage service="organizations" client:load />
Loading