Amazon S3 Files: Security Model Deep Dive

8 min read

Amazon S3 Files went GA in April 2026. It lets you mount an S3 bucket as a proper NFS file system on EC2 — ls, cat, vim, the works. Your data never leaves S3, but applications interact with it using standard POSIX file operations instead of the S3 API.

That's a big deal for workloads that expect a filesystem (legacy apps, ML training pipelines, shared datasets). But it also means you now have two access planes to the same data: the S3 API and the NFS mount. Each has its own authorization model, and they don't automatically agree with each other.

This post focuses on the security side: how the layers stack, where the gaps are, and how to lock it down properly.

The Security Layer Cake

S3 Files has five layers of access control. A request must pass all of them to succeed:

  1. IAM identity policies — what the caller's role/user is allowed to do
  2. File system resource policies — what the file system itself allows
  3. S3 bucket policies — what the underlying bucket allows (for read-path optimization)
  4. POSIX permissions — Unix-style uid/gid/mode bits on files and directories
  5. Security groups — network-level firewall between compute and mount targets
Diagram showing five stacked layers: IAM identity policy at the top, then file system resource policy, then S3 bucket policy, then POSIX permissions, then security groups at the bottom. An arrow labeled 'NFS mount request' passes through all five.
Every file operation must pass all five layers. A deny at any layer blocks the request.

Miss any one of these and you'll either lock yourself out or leave a hole. Let's go through each.

Layer 1: IAM Identity Policies

S3 Files introduces three new IAM actions for client access (the NFS mount path):

ActionWhat it grants
s3files:ClientMountRead-only access to the file system
s3files:ClientWriteWrite access to the file system
s3files:ClientRootAccessRoot user (uid 0) access on the file system

These are separate from the S3 API actions (s3:GetObject, s3:PutObject, etc.). A role can have full S3 API access to the bucket but zero NFS access if it lacks the s3files:Client* actions.

AWS provides managed policies to get started:

  • AmazonS3FilesFullAccess — manage file systems, mount targets, access points
  • AmazonS3FilesReadOnlyAccess — view configurations only
  • AmazonS3FilesClientFullAccess — mount, read, write, and root access

Least-Privilege Example

For an application that needs to read and write files but should never operate as root:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3files:ClientMount",
        "s3files:ClientWrite"
      ],
      "Resource": "arn:aws:s3files:eu-west-2:111122223333:file-system/fs-0abc123def456"
    }
  ]
}

No s3files:ClientRootAccess means the mount operates as the POSIX user mapped to the connecting identity — not root. Files owned by root are read-only to this role.

The Read-Path Optimization

S3 Files has an "intelligent read routing" feature that reads file data directly from the underlying S3 bucket for performance. For this to work, the EC2 instance's IAM role also needs s3:GetObject on the bucket:

{
  "Effect": "Allow",
  "Action": "s3:GetObject",
  "Resource": "arn:aws:s3:::my-bucket/*"
}

The mount helper adds this automatically if you use the recommended setup, but it's worth knowing it exists — it means the role has S3 API read access to the bucket, not just NFS access.

Layer 2: File System Resource Policies

Like S3 bucket policies, S3 Files supports resource-based policies attached directly to the file system. These control what clients can do once connected.

A file system with no explicit policy uses a permissive default. Always attach an explicit policy.

Read-Only Policy

{
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111122223333:role/AppReadOnly"
      },
      "Action": [
        "s3files:ClientMount"
      ]
    }
  ]
}

This role can mount and read. It cannot write or access files as root.

Deny Root Access Globally

{
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "AWS": "*" },
      "Action": [
        "s3files:ClientMount",
        "s3files:ClientWrite"
      ]
    },
    {
      "Effect": "Deny",
      "Principal": { "AWS": "*" },
      "Action": "s3files:ClientRootAccess",
      "Condition": {
        "StringNotEquals": {
          "aws:PrincipalArn": "arn:aws:iam::111122223333:role/FileSystemAdmin"
        }
      }
    }
  ]
}

Everyone can mount and write, but only FileSystemAdmin gets root. This is the pattern I'd recommend for most setups.

Access Points

S3 Files access points let you scope access further. Each access point can enforce a specific POSIX identity (uid/gid) for all requests through it, regardless of the connecting role. Use the s3files:AccessPointArn condition key to restrict which access point a policy applies to.

Diagram showing two access points on the same file system: one for the app role (uid 1000, read/write) and one for the analytics role (uid 2000, read-only). Both connect to the same underlying file system.
Access points let you assign different POSIX identities and permissions to different consumers of the same file system.

Layer 3: S3 Bucket Policies

Your S3 bucket still has its own bucket policy. Because S3 Files reads data from the bucket (for the intelligent read routing path), the bucket policy must not deny access from the compute role or the file system.

Common mistake: a bucket policy with an explicit Deny on s3:GetObject for principals outside a specific VPC endpoint will block the read-path optimization. The file system itself accesses the bucket from within AWS's infrastructure, not from your VPC endpoint.

Layer 4: POSIX Permissions

After IAM authorization succeeds, S3 Files enforces standard Unix permissions: owner, group, others, with read/write/execute bits. This is the same model as any Linux filesystem.

Key points:

  • New files inherit the default permissions based on the creating process's umask
  • Root access (uid 0) bypasses POSIX permission checks — which is why s3files:ClientRootAccess is so sensitive
  • Access points can force a specific uid/gid for all operations, overriding whatever the connecting role would normally map to
  • Ownership matters: if your app creates files as uid 1000 and another service connects as uid 2000, the second service can't read those files unless group or other permissions allow it

Practical Pattern: Shared Dataset

For a shared ML training dataset where multiple teams need read access but only the data pipeline should write:

  1. Data pipeline role → access point with uid 1000, ClientMount + ClientWrite
  2. Training roles → access point with uid 2000, ClientMount only
  3. Set directory permissions to 755 (owner rwx, group/other rx)
  4. Set file permissions to 644 (owner rw, group/other r)

The training roles can read everything but can't modify the dataset.

Layer 5: Security Groups

S3 Files uses mount targets (similar to EFS) that live in your VPC subnets. Security groups control which compute resources can reach these mount targets over the network.

resource "aws_security_group" "s3_files_mount_target" {
  name_prefix = "s3-files-mt-"
  vpc_id      = aws_vpc.main.id
 
  ingress {
    from_port       = 2049
    to_port         = 2049
    protocol        = "tcp"
    security_groups = [aws_security_group.app_instances.id]
    description     = "NFS from app instances only"
  }
}
VPC diagram showing an app subnet with EC2 instances, a mount target in the same AZ with a security group allowing TCP 2049 only from the app security group, and the S3 bucket in the background.
Security groups on mount targets are your network perimeter. Instances outside the allowed security groups can't even reach the NFS port.

Encryption

At Rest

S3 Files encrypts all data and metadata at rest automatically. Two options:

  • AWS-owned KMS key (default) — zero config, no cost, AWS manages the key lifecycle
  • Customer-managed KMS key — you control the key policy, rotation, and can revoke access by disabling the key

For regulated workloads, use a customer-managed key. It gives you an independent kill switch: disable the KMS key and the file system becomes unreadable, even if IAM policies still allow access.

resource "aws_kms_key" "s3_files" {
  description         = "S3 Files encryption key"
  enable_key_rotation = true
 
  policy = data.aws_iam_policy_document.s3_files_key_policy.json
}

In Transit

Data between your EC2 instance and the mount target is encrypted in transit using TLS. The S3 Files mount helper handles this transparently — you don't need to configure certificates or TLS settings.

The intelligent read routing path (direct reads from S3) also uses TLS. All data paths are encrypted in transit by default with no opt-out.

Common Security Mistakes

1. Using the FullAccess Managed Policy for Applications

AmazonS3FilesClientFullAccess grants root. Your app doesn't need root. Write a custom policy with only ClientMount and ClientWrite.

2. No Explicit File System Policy

Without one, the default is permissive. Always attach a policy that explicitly names which principals get which actions.

3. Forgetting the Bucket Policy Interaction

The S3 bucket policy and the file system policy are independent. A restrictive bucket policy can break the read-path optimization without any obvious error — reads just get slower.

4. Wide-Open Security Groups

Don't allow 0.0.0.0/0 on port 2049. Scope inbound NFS to the specific security groups of instances that need to mount.

5. Ignoring POSIX Permissions

IAM says "yes" but POSIX says "no" → access denied. If your app creates files as one uid and another service reads as a different uid, you'll get silent permission failures. Use access points to enforce consistent identities.

Audit and Monitoring

S3 Files operations appear in CloudTrail. Look for:

  • s3files:CreateFileSystem, s3files:DeleteFileSystem — lifecycle events
  • s3files:CreateMountTarget — new network exposure
  • s3files:PutFileSystemPolicy — policy changes

For data-plane monitoring (who's reading/writing what), enable S3 server access logging on the underlying bucket. NFS reads that go through the intelligent read routing path show up as GetObject calls from the file system's internal role.

Wrapping Up

S3 Files is genuinely useful — it removes the "do I use EFS or S3?" decision for many workloads. But the security model is more complex than either EFS or plain S3 alone because you're layering NFS access control on top of S3's existing model.

The short version:

  1. Write least-privilege IAM policies — no ClientRootAccess for apps
  2. Attach an explicit file system resource policy — don't rely on defaults
  3. Check your bucket policy doesn't block the read-path optimization
  4. Use access points to enforce POSIX identities per consumer
  5. Lock down security groups to port 2049 from specific source groups
  6. Use a customer-managed KMS key if you need an independent kill switch
  7. Monitor CloudTrail for s3files:* events

Get those right and you've got a filesystem that's as secure as your S3 bucket — which, if you've been following AWS best practices, should be pretty locked down already.