> For the complete documentation index, see [llms.txt](https://docs.eazybackup.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.eazybackup.com/e3-object-storage/creating-an-e3-bucket-policy-to-restrict-access-by-ip-address-or-subnet.md).

# Creating an e3 Bucket Policy to Restrict Access by IP Address or Subnet

In this guide we will explain how to create an e3 bucket policy that restricts access to a bucket so that data requests are only permitted from a single IP address or an approved range of IP addresses (a subnet). Requests originating from any other address will be denied.

This is a common way to "fence in" a bucket so that only your office network, a specific server, or a known VPN egress address can read from or write to it.

> **Important — do not restrict `s3:*` while setting this up.** Denying every action (`"Action": "s3:*"`) from non-approved addresses also denies `s3:PutBucketPolicy` and `s3:DeleteBucketPolicy`. If the service does not see the source IP you expect, you can lock yourself out of bucket-policy management entirely with no way to self-recover. The safe approach in this guide restricts **data-plane actions only** (reads, writes, lists) and deliberately leaves the policy-management actions out of the Deny so you can always correct or remove the rule.

### Basic Policy Structure

This policy uses the same JSON structure described in *"*[*Understanding and Creating a Basic e3 Bucket Policy.*](/e3-object-storage/understanding-and-creating-a-basic-e3-bucket-policy.md)*"* Make sure you are familiar with `Version`, `Statement`, `Sid`, `Effect`, `Principal`, `Action`, and `Resource`.

To restrict by IP, we add one extra element: a `Condition` block that evaluates the source address of each request using the `aws:SourceIp` condition key.

### The Logic: Deny data access if the request is NOT from an approved address

The most reliable way to enforce an IP allow-list is with a **Deny** rule using the `NotIpAddress` condition. The logic is: *"Deny the data operations on this bucket if the request did NOT come from one of my approved addresses."*

A Deny rule is preferred over an Allow rule here because in policy evaluation an explicit Deny always wins. Even if some other policy or permission would otherwise grant access, the IP restriction still holds — it "fails closed."

Crucially, we apply the Deny only to **data-plane actions** (listing the bucket, and reading/writing/deleting objects). We intentionally **do not** include the policy-management actions (`s3:GetBucketPolicy`, `s3:PutBucketPolicy`, `s3:DeleteBucketPolicy`) in the Deny. Keeping those outside the rule means that even if the IP condition behaves unexpectedly, you can still read, fix, or remove the policy and recover access.

### How IP addresses and ranges are written

The `aws:SourceIp` value uses standard CIDR notation:

| You want to allow                                        | Write it as                                 |
| -------------------------------------------------------- | ------------------------------------------- |
| A single IPv4 address (e.g. `203.0.113.50`)              | `203.0.113.50/32`                           |
| A small range / subnet (e.g. a /24 block, 256 addresses) | `203.0.113.0/24`                            |
| A larger private range                                   | `10.0.0.0/8`                                |
| Multiple separate addresses or ranges                    | List each as a separate string in the array |

A `/32` suffix means "this exact address only." Lower numbers (e.g. `/24`, `/16`, `/8`) widen the range.

### Recommended Policy: Restrict Data Access to a Single IP Address

This is the **safe default** for IP restriction. It denies bucket-listing and object read/write/delete operations on `your-bucket-name` unless the request comes from `203.0.113.50`. Policy-management actions are left out of the Deny so you can always recover.

Replace the placeholders:

* `your-bucket-name` with the name of your bucket.
* `203.0.113.50/32` with the public IP address you want to allow.

**Policy JSON:**

```json
{
  "Version": "2012-10-17",
  "Id": "RestrictDataAccessToSingleIP",
  "Statement": [
    {
      "Sid": "DenyBucketReadsOutsideApprovedIP",
      "Effect": "Deny",
      "Principal": "*",
      "Action": [
        "s3:ListBucket",
        "s3:ListBucketVersions",
        "s3:ListBucketMultipartUploads",
        "s3:GetBucketLocation"
      ],
      "Resource": "arn:aws:s3:::your-bucket-name",
      "Condition": {
        "NotIpAddress": {
          "aws:SourceIp": "203.0.113.50/32"
        }
      }
    },
    {
      "Sid": "DenyObjectAccessOutsideApprovedIP",
      "Effect": "Deny",
      "Principal": "*",
      "Action": [
        "s3:GetObject",
        "s3:GetObjectVersion",
        "s3:PutObject",
        "s3:DeleteObject",
        "s3:DeleteObjectVersion",
        "s3:AbortMultipartUpload",
        "s3:ListMultipartUploadParts",
        "s3:GetObjectAcl",
        "s3:PutObjectAcl"
      ],
      "Resource": "arn:aws:s3:::your-bucket-name/*",
      "Condition": {
        "NotIpAddress": {
          "aws:SourceIp": "203.0.113.50/32"
        }
      }
    }
  ]
}
```

#### Explanation of the Recommended Policy

* **Two statements, two resource scopes.** Bucket-level actions (like `ListBucket`) target the bucket ARN `arn:aws:s3:::your-bucket-name`, while object-level actions (like `GetObject`/`PutObject`) target the objects ARN `arn:aws:s3:::your-bucket-name/*`. Splitting them keeps each action paired with the correct resource.
* `"Effect": "Deny"`: Each rule blocks the listed actions when its condition is met.
* `"Principal": "*"`: The rule applies to all users. To scope it to one specific user instead, replace `"*"` with `{ "AWS": ["arn:aws:iam::your-account-id:user/your-username"] }`.
* **Data-plane actions only.** The two statements cover listing, reading, writing, deleting, multipart uploads, and object ACLs — the operations you actually want fenced to an IP. They deliberately exclude `s3:GetBucketPolicy`, `s3:PutBucketPolicy`, and `s3:DeleteBucketPolicy`, so policy management remains possible from any address and you retain a recovery path.
* `"Condition": { "NotIpAddress": { "aws:SourceIp": "203.0.113.50/32" } }`: The Deny applies only when the request's source IP is **not** `203.0.113.50`.
  * A request from `203.0.113.50` → does **not** match `NotIpAddress` → the Deny does **not** apply → access proceeds.
  * A request from any other address → matches `NotIpAddress` → the Deny **applies** → access is blocked.

### Restrict to an IP Range / Subnet

To allow a whole subnet instead of a single host, change the CIDR suffix on **both** statements. For example, allow the entire `203.0.113.0/24` block (addresses `203.0.113.0`–`203.0.113.255`) by setting:

```json
"aws:SourceIp": "203.0.113.0/24"
```

in each `Condition` block of the recommended policy above.

### Allow Several Addresses or Ranges

To permit multiple locations — for example, an office IP and a server subnet — supply an array of CIDR values in each `Condition` block. A request is allowed if it matches **any** entry in the list:

```json
"Condition": {
  "NotIpAddress": {
    "aws:SourceIp": [
      "203.0.113.50/32",
      "198.51.100.0/24",
      "192.0.2.10/32"
    ]
  }
}
```

The Deny applies only when the source IP is **not** in any of the listed entries. Remember to update the `Condition` in **both** statements (bucket-level and object-level) so the allow-list stays consistent.

### Applying the Policy with the AWS CLI

The e3 service is S3-compatible, so you can manage bucket policies with the standard AWS CLI by pointing it at the e3 endpoint.

#### 1. Configure the AWS CLI (one time)

If you have not configured a profile yet, run:

```bash
aws configure --profile e3
```

Enter the **Access Key** and **Secret Key** from your e3 **Access Keys** page when prompted. For the default region you can enter a placeholder such as `ca-central-1`; the region value is not used to route requests when you specify an endpoint.

#### 2. Confirm the public IP you will allow

Before you write the IP into the policy, confirm the public address your traffic actually leaves from. Run this on the machine that should retain access:

```bash
curl -4 https://checkip.amazonaws.com
```

The address returned is the one e3 evaluates against your policy. Put **that** value in `aws:SourceIp`.

#### 3. Save the policy to a file

Save your chosen policy JSON to a file, for example `restrict-ip-policy.json`.

#### 4. Apply the policy

Run `put-bucket-policy`, pointing `--endpoint-url` at your e3 Service URL (shown on the **Access Keys** page):

```bash
aws s3api put-bucket-policy \
  --bucket your-bucket-name \
  --policy file://restrict-ip-policy.json \
  --endpoint-url https://s3.ca-central-1.eazybackup.com \
  --profile e3
```

If the command returns no output, the policy was accepted. (The AWS CLI prints nothing on a successful `put-bucket-policy`.)

### Confirming the Policy Was Applied

Retrieve the policy currently attached to the bucket:

```bash
aws s3api get-bucket-policy \
  --bucket your-bucket-name \
  --endpoint-url https://s3.ca-central-1.eazybackup.com \
  --profile e3
```

This returns the policy document as a JSON string. Confirm it matches what you applied — check the bucket name, the `aws:SourceIp` values, and the `Effect`.

Because the recommended policy leaves `s3:GetBucketPolicy` out of the Deny, this command works from any address, which is exactly the recovery path you want.

To view it as readable formatted JSON (if you have the `jq` tool installed):

```bash
aws s3api get-bucket-policy \
  --bucket your-bucket-name \
  --endpoint-url https://s3.ca-central-1.eazybackup.com \
  --profile e3 \
  --query Policy --output text | jq .
```

### Testing That the Policy Works

Testing is essential. You should verify both the "allowed" and "blocked" paths. The full sequence — confirm your IP, apply, test access, and confirm the policy — looks like this:

```bash
curl -4 https://checkip.amazonaws.com

aws s3api put-bucket-policy \
  --bucket your-bucket-name \
  --policy file://restrict-ip-policy.json \
  --endpoint-url https://s3.ca-central-1.eazybackup.com \
  --profile e3

aws s3 ls s3://your-bucket-name \
  --endpoint-url https://s3.ca-central-1.eazybackup.com \
  --profile e3

aws s3api get-bucket-policy \
  --bucket your-bucket-name \
  --endpoint-url https://s3.ca-central-1.eazybackup.com \
  --profile e3
```

#### Test 1 — From an approved address (should succeed)

From a machine using one of your allowed IPs, the list command above should return the bucket contents normally.

#### Test 2 — From a non-approved address (should be denied)

From a machine **not** on an approved IP (for example, a home connection, a mobile hotspot, or a cloud VM in a different network), run the same list command:

```bash
aws s3 ls s3://your-bucket-name \
  --endpoint-url https://s3.ca-central-1.eazybackup.com \
  --profile e3
```

This should fail with an **`AccessDenied`** error, confirming the restriction is active. Meanwhile, `get-bucket-policy` should still succeed from this same non-approved address — confirming your recovery path is intact.

### Important Notes

#### Use the correct *public* IP address

`aws:SourceIp` is matched against the public IP that e3 sees on the incoming connection. If your servers sit behind NAT, a firewall, or a VPN, the address e3 observes is the **public egress IP** of that network — not the internal/private LAN address of the machine (e.g. not `192.168.x.x` or `10.x.x.x`). Always allow-list the public address your traffic actually leaves from. Confirm it with `curl -4 https://checkip.amazonaws.com` from the source machine.

#### Avoid locking yourself out

The recommended policy is designed so you cannot lose access to policy management — `s3:GetBucketPolicy`, `s3:PutBucketPolicy`, and `s3:DeleteBucketPolicy` are never inside the Deny. Still, to stay safe:

* Double-check your approved IP/CIDR values **before** applying.
* Apply and test from a machine that is already on an approved address.
* Consider temporarily including your admin workstation's IP in the allow-list while you validate, then tighten it afterward.
* If you ever do get locked out, contact eazyBackup support — our team may be able to assist with removing or correcting the policy on the back end.

> **Do not use `"Action": "s3:*"` while testing IP restrictions.** If the service does not see the expected client IP, this can also deny `PutBucketPolicy` and `DeleteBucketPolicy`, preventing self-service recovery. Restrict the specific data-plane actions shown above instead.

#### Watch out for dynamic IP addresses

Many residential and small-office internet connections use IP addresses that change periodically. If you restrict to a single dynamic IP, access will break when that address changes. For these situations, allow the provider's broader assigned range, use a static IP, or route traffic through a fixed VPN/proxy egress address.

#### `Deny` always wins

If you also have Allow policies on the bucket, remember that an explicit Deny overrides any Allow. The IP restriction above takes precedence for the data-plane actions it covers — which is the intended, fail-closed behaviour.

#### Advanced (not recommended): denying all actions with `s3:*`

Some administrators want to deny **every** S3 action from non-approved IPs, including policy management, for the strictest possible lockdown. This is an advanced configuration and carries a real lockout risk: if we do not observe the source IP you expect, a `s3:*` Deny will also block `PutBucketPolicy` and `DeleteBucketPolicy`, leaving **no self-service way to fix or remove the policy**. Only do this if you fully control and have verified the source-IP path end to end, and you understand that recovery may require contacting eazyBackup support. For nearly all use cases, the data-plane policy shown earlier provides the protection you want without the lockout risk.
