MyFSIO v0.2.2 Release #14
@@ -72,13 +72,11 @@ def _evaluate_condition_operator(
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
ACTION_ALIASES = {
|
ACTION_ALIASES = {
|
||||||
# List actions
|
|
||||||
"s3:listbucket": "list",
|
"s3:listbucket": "list",
|
||||||
"s3:listallmybuckets": "list",
|
"s3:listallmybuckets": "list",
|
||||||
"s3:listbucketversions": "list",
|
"s3:listbucketversions": "list",
|
||||||
"s3:listmultipartuploads": "list",
|
"s3:listmultipartuploads": "list",
|
||||||
"s3:listparts": "list",
|
"s3:listparts": "list",
|
||||||
# Read actions
|
|
||||||
"s3:getobject": "read",
|
"s3:getobject": "read",
|
||||||
"s3:getobjectversion": "read",
|
"s3:getobjectversion": "read",
|
||||||
"s3:getobjecttagging": "read",
|
"s3:getobjecttagging": "read",
|
||||||
@@ -87,7 +85,6 @@ ACTION_ALIASES = {
|
|||||||
"s3:getbucketversioning": "read",
|
"s3:getbucketversioning": "read",
|
||||||
"s3:headobject": "read",
|
"s3:headobject": "read",
|
||||||
"s3:headbucket": "read",
|
"s3:headbucket": "read",
|
||||||
# Write actions
|
|
||||||
"s3:putobject": "write",
|
"s3:putobject": "write",
|
||||||
"s3:createbucket": "write",
|
"s3:createbucket": "write",
|
||||||
"s3:putobjecttagging": "write",
|
"s3:putobjecttagging": "write",
|
||||||
@@ -97,26 +94,30 @@ ACTION_ALIASES = {
|
|||||||
"s3:completemultipartupload": "write",
|
"s3:completemultipartupload": "write",
|
||||||
"s3:abortmultipartupload": "write",
|
"s3:abortmultipartupload": "write",
|
||||||
"s3:copyobject": "write",
|
"s3:copyobject": "write",
|
||||||
# Delete actions
|
|
||||||
"s3:deleteobject": "delete",
|
"s3:deleteobject": "delete",
|
||||||
"s3:deleteobjectversion": "delete",
|
"s3:deleteobjectversion": "delete",
|
||||||
"s3:deletebucket": "delete",
|
"s3:deletebucket": "delete",
|
||||||
"s3:deleteobjecttagging": "delete",
|
"s3:deleteobjecttagging": "delete",
|
||||||
# Share actions (ACL)
|
|
||||||
"s3:putobjectacl": "share",
|
"s3:putobjectacl": "share",
|
||||||
"s3:putbucketacl": "share",
|
"s3:putbucketacl": "share",
|
||||||
"s3:getbucketacl": "share",
|
"s3:getbucketacl": "share",
|
||||||
# Policy actions
|
|
||||||
"s3:putbucketpolicy": "policy",
|
"s3:putbucketpolicy": "policy",
|
||||||
"s3:getbucketpolicy": "policy",
|
"s3:getbucketpolicy": "policy",
|
||||||
"s3:deletebucketpolicy": "policy",
|
"s3:deletebucketpolicy": "policy",
|
||||||
# Replication actions
|
|
||||||
"s3:getreplicationconfiguration": "replication",
|
"s3:getreplicationconfiguration": "replication",
|
||||||
"s3:putreplicationconfiguration": "replication",
|
"s3:putreplicationconfiguration": "replication",
|
||||||
"s3:deletereplicationconfiguration": "replication",
|
"s3:deletereplicationconfiguration": "replication",
|
||||||
"s3:replicateobject": "replication",
|
"s3:replicateobject": "replication",
|
||||||
"s3:replicatetags": "replication",
|
"s3:replicatetags": "replication",
|
||||||
"s3:replicatedelete": "replication",
|
"s3:replicatedelete": "replication",
|
||||||
|
"s3:getlifecycleconfiguration": "lifecycle",
|
||||||
|
"s3:putlifecycleconfiguration": "lifecycle",
|
||||||
|
"s3:deletelifecycleconfiguration": "lifecycle",
|
||||||
|
"s3:getbucketlifecycle": "lifecycle",
|
||||||
|
"s3:putbucketlifecycle": "lifecycle",
|
||||||
|
"s3:getbucketcors": "cors",
|
||||||
|
"s3:putbucketcors": "cors",
|
||||||
|
"s3:deletebucketcors": "cors",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
12
app/iam.py
12
app/iam.py
@@ -15,7 +15,7 @@ class IamError(RuntimeError):
|
|||||||
"""Raised when authentication or authorization fails."""
|
"""Raised when authentication or authorization fails."""
|
||||||
|
|
||||||
|
|
||||||
S3_ACTIONS = {"list", "read", "write", "delete", "share", "policy", "replication"}
|
S3_ACTIONS = {"list", "read", "write", "delete", "share", "policy", "replication", "lifecycle", "cors"}
|
||||||
IAM_ACTIONS = {
|
IAM_ACTIONS = {
|
||||||
"iam:list_users",
|
"iam:list_users",
|
||||||
"iam:create_user",
|
"iam:create_user",
|
||||||
@@ -71,6 +71,16 @@ ACTION_ALIASES = {
|
|||||||
"s3:replicateobject": "replication",
|
"s3:replicateobject": "replication",
|
||||||
"s3:replicatetags": "replication",
|
"s3:replicatetags": "replication",
|
||||||
"s3:replicatedelete": "replication",
|
"s3:replicatedelete": "replication",
|
||||||
|
"lifecycle": "lifecycle",
|
||||||
|
"s3:getlifecycleconfiguration": "lifecycle",
|
||||||
|
"s3:putlifecycleconfiguration": "lifecycle",
|
||||||
|
"s3:deletelifecycleconfiguration": "lifecycle",
|
||||||
|
"s3:getbucketlifecycle": "lifecycle",
|
||||||
|
"s3:putbucketlifecycle": "lifecycle",
|
||||||
|
"cors": "cors",
|
||||||
|
"s3:getbucketcors": "cors",
|
||||||
|
"s3:putbucketcors": "cors",
|
||||||
|
"s3:deletebucketcors": "cors",
|
||||||
"iam:listusers": "iam:list_users",
|
"iam:listusers": "iam:list_users",
|
||||||
"iam:createuser": "iam:create_user",
|
"iam:createuser": "iam:create_user",
|
||||||
"iam:deleteuser": "iam:delete_user",
|
"iam:deleteuser": "iam:delete_user",
|
||||||
|
|||||||
25
app/ui.py
25
app/ui.py
@@ -381,6 +381,23 @@ def bucket_detail(bucket_name: str):
|
|||||||
can_edit_policy = True
|
can_edit_policy = True
|
||||||
except IamError:
|
except IamError:
|
||||||
can_edit_policy = False
|
can_edit_policy = False
|
||||||
|
|
||||||
|
can_manage_lifecycle = False
|
||||||
|
if principal:
|
||||||
|
try:
|
||||||
|
_iam().authorize(principal, bucket_name, "lifecycle")
|
||||||
|
can_manage_lifecycle = True
|
||||||
|
except IamError:
|
||||||
|
can_manage_lifecycle = False
|
||||||
|
|
||||||
|
can_manage_cors = False
|
||||||
|
if principal:
|
||||||
|
try:
|
||||||
|
_iam().authorize(principal, bucket_name, "cors")
|
||||||
|
can_manage_cors = True
|
||||||
|
except IamError:
|
||||||
|
can_manage_cors = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
versioning_enabled = storage.is_versioning_enabled(bucket_name)
|
versioning_enabled = storage.is_versioning_enabled(bucket_name)
|
||||||
except StorageError:
|
except StorageError:
|
||||||
@@ -452,6 +469,8 @@ def bucket_detail(bucket_name: str):
|
|||||||
bucket_policy_text=policy_text,
|
bucket_policy_text=policy_text,
|
||||||
bucket_policy=bucket_policy,
|
bucket_policy=bucket_policy,
|
||||||
can_edit_policy=can_edit_policy,
|
can_edit_policy=can_edit_policy,
|
||||||
|
can_manage_lifecycle=can_manage_lifecycle,
|
||||||
|
can_manage_cors=can_manage_cors,
|
||||||
can_manage_versioning=can_manage_versioning,
|
can_manage_versioning=can_manage_versioning,
|
||||||
can_manage_replication=can_manage_replication,
|
can_manage_replication=can_manage_replication,
|
||||||
can_manage_encryption=can_manage_encryption,
|
can_manage_encryption=can_manage_encryption,
|
||||||
@@ -2128,7 +2147,7 @@ def metrics_api():
|
|||||||
def bucket_lifecycle(bucket_name: str):
|
def bucket_lifecycle(bucket_name: str):
|
||||||
principal = _current_principal()
|
principal = _current_principal()
|
||||||
try:
|
try:
|
||||||
_authorize_ui(principal, bucket_name, "policy")
|
_authorize_ui(principal, bucket_name, "lifecycle")
|
||||||
except IamError as exc:
|
except IamError as exc:
|
||||||
return jsonify({"error": str(exc)}), 403
|
return jsonify({"error": str(exc)}), 403
|
||||||
|
|
||||||
@@ -2181,7 +2200,7 @@ def bucket_lifecycle(bucket_name: str):
|
|||||||
def get_lifecycle_history(bucket_name: str):
|
def get_lifecycle_history(bucket_name: str):
|
||||||
principal = _current_principal()
|
principal = _current_principal()
|
||||||
try:
|
try:
|
||||||
_authorize_ui(principal, bucket_name, "policy")
|
_authorize_ui(principal, bucket_name, "lifecycle")
|
||||||
except IamError:
|
except IamError:
|
||||||
return jsonify({"error": "Access denied"}), 403
|
return jsonify({"error": "Access denied"}), 403
|
||||||
|
|
||||||
@@ -2212,7 +2231,7 @@ def get_lifecycle_history(bucket_name: str):
|
|||||||
def bucket_cors(bucket_name: str):
|
def bucket_cors(bucket_name: str):
|
||||||
principal = _current_principal()
|
principal = _current_principal()
|
||||||
try:
|
try:
|
||||||
_authorize_ui(principal, bucket_name, "policy")
|
_authorize_ui(principal, bucket_name, "cors")
|
||||||
except IamError as exc:
|
except IamError as exc:
|
||||||
return jsonify({"error": str(exc)}), 403
|
return jsonify({"error": str(exc)}), 403
|
||||||
|
|
||||||
|
|||||||
233
docs.md
233
docs.md
@@ -602,6 +602,10 @@ fi
|
|||||||
|
|
||||||
## 4. Authentication & IAM
|
## 4. Authentication & IAM
|
||||||
|
|
||||||
|
MyFSIO implements a comprehensive Identity and Access Management (IAM) system that controls who can access your buckets and what operations they can perform. The system supports both simple action-based permissions and AWS-compatible policy syntax.
|
||||||
|
|
||||||
|
### Getting Started
|
||||||
|
|
||||||
1. On first boot, `data/.myfsio.sys/config/iam.json` is seeded with `localadmin / localadmin` that has wildcard access.
|
1. On first boot, `data/.myfsio.sys/config/iam.json` is seeded with `localadmin / localadmin` that has wildcard access.
|
||||||
2. Sign into the UI using those credentials, then open **IAM**:
|
2. Sign into the UI using those credentials, then open **IAM**:
|
||||||
- **Create user**: supply a display name and optional JSON inline policy array.
|
- **Create user**: supply a display name and optional JSON inline policy array.
|
||||||
@@ -609,48 +613,241 @@ fi
|
|||||||
- **Policy editor**: select a user, paste an array of objects (`{"bucket": "*", "actions": ["list", "read"]}`), and submit. Alias support includes AWS-style verbs (e.g., `s3:GetObject`).
|
- **Policy editor**: select a user, paste an array of objects (`{"bucket": "*", "actions": ["list", "read"]}`), and submit. Alias support includes AWS-style verbs (e.g., `s3:GetObject`).
|
||||||
3. Wildcard action `iam:*` is supported for admin user definitions.
|
3. Wildcard action `iam:*` is supported for admin user definitions.
|
||||||
|
|
||||||
The API expects every request to include `X-Access-Key` and `X-Secret-Key` headers. The UI persists them in the Flask session after login.
|
### Authentication
|
||||||
|
|
||||||
|
The API expects every request to include authentication headers. The UI persists them in the Flask session after login.
|
||||||
|
|
||||||
|
| Header | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `X-Access-Key` | The user's access key identifier |
|
||||||
|
| `X-Secret-Key` | The user's secret key for signing |
|
||||||
|
|
||||||
|
**Security Features:**
|
||||||
|
- **Lockout Protection**: After `AUTH_MAX_ATTEMPTS` (default: 5) failed login attempts, the account is locked for `AUTH_LOCKOUT_MINUTES` (default: 15 minutes).
|
||||||
|
- **Session Management**: UI sessions remain valid for `SESSION_LIFETIME_DAYS` (default: 30 days).
|
||||||
|
- **Hot Reload**: IAM configuration changes take effect immediately without restart.
|
||||||
|
|
||||||
|
### Permission Model
|
||||||
|
|
||||||
|
MyFSIO uses a two-layer permission model:
|
||||||
|
|
||||||
|
1. **IAM User Policies** – Define what a user can do across the system (stored in `iam.json`)
|
||||||
|
2. **Bucket Policies** – Define who can access a specific bucket (stored in `bucket_policies.json`)
|
||||||
|
|
||||||
|
Both layers are evaluated for each request. A user must have permission in their IAM policy AND the bucket policy must allow the action (or have no explicit deny).
|
||||||
|
|
||||||
### Available IAM Actions
|
### Available IAM Actions
|
||||||
|
|
||||||
|
#### S3 Actions (Bucket/Object Operations)
|
||||||
|
|
||||||
| Action | Description | AWS Aliases |
|
| Action | Description | AWS Aliases |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `list` | List buckets and objects | `s3:ListBucket`, `s3:ListAllMyBuckets`, `s3:ListBucketVersions`, `s3:ListMultipartUploads`, `s3:ListParts` |
|
| `list` | List buckets and objects | `s3:ListBucket`, `s3:ListAllMyBuckets`, `s3:ListBucketVersions`, `s3:ListMultipartUploads`, `s3:ListParts` |
|
||||||
| `read` | Download objects | `s3:GetObject`, `s3:GetObjectVersion`, `s3:GetObjectTagging`, `s3:HeadObject`, `s3:HeadBucket` |
|
| `read` | Download objects, get metadata | `s3:GetObject`, `s3:GetObjectVersion`, `s3:GetObjectTagging`, `s3:GetObjectVersionTagging`, `s3:GetObjectAcl`, `s3:GetBucketVersioning`, `s3:HeadObject`, `s3:HeadBucket` |
|
||||||
| `write` | Upload objects, create buckets | `s3:PutObject`, `s3:CreateBucket`, `s3:CreateMultipartUpload`, `s3:UploadPart`, `s3:CompleteMultipartUpload`, `s3:AbortMultipartUpload`, `s3:CopyObject` |
|
| `write` | Upload objects, create buckets, manage tags | `s3:PutObject`, `s3:CreateBucket`, `s3:PutObjectTagging`, `s3:PutBucketVersioning`, `s3:CreateMultipartUpload`, `s3:UploadPart`, `s3:CompleteMultipartUpload`, `s3:AbortMultipartUpload`, `s3:CopyObject` |
|
||||||
| `delete` | Remove objects and buckets | `s3:DeleteObject`, `s3:DeleteObjectVersion`, `s3:DeleteBucket` |
|
| `delete` | Remove objects, versions, and buckets | `s3:DeleteObject`, `s3:DeleteObjectVersion`, `s3:DeleteBucket`, `s3:DeleteObjectTagging` |
|
||||||
| `share` | Manage ACLs | `s3:PutObjectAcl`, `s3:PutBucketAcl`, `s3:GetBucketAcl` |
|
| `share` | Manage Access Control Lists (ACLs) | `s3:PutObjectAcl`, `s3:PutBucketAcl`, `s3:GetBucketAcl` |
|
||||||
| `policy` | Manage bucket policies | `s3:PutBucketPolicy`, `s3:GetBucketPolicy`, `s3:DeleteBucketPolicy` |
|
| `policy` | Manage bucket policies | `s3:PutBucketPolicy`, `s3:GetBucketPolicy`, `s3:DeleteBucketPolicy` |
|
||||||
| `replication` | Configure and manage replication | `s3:GetReplicationConfiguration`, `s3:PutReplicationConfiguration`, `s3:ReplicateObject`, `s3:ReplicateTags`, `s3:ReplicateDelete` |
|
| `lifecycle` | Manage lifecycle rules | `s3:GetLifecycleConfiguration`, `s3:PutLifecycleConfiguration`, `s3:DeleteLifecycleConfiguration`, `s3:GetBucketLifecycle`, `s3:PutBucketLifecycle` |
|
||||||
| `iam:list_users` | View IAM users | `iam:ListUsers` |
|
| `cors` | Manage CORS configuration | `s3:GetBucketCors`, `s3:PutBucketCors`, `s3:DeleteBucketCors` |
|
||||||
| `iam:create_user` | Create IAM users | `iam:CreateUser` |
|
| `replication` | Configure and manage replication | `s3:GetReplicationConfiguration`, `s3:PutReplicationConfiguration`, `s3:DeleteReplicationConfiguration`, `s3:ReplicateObject`, `s3:ReplicateTags`, `s3:ReplicateDelete` |
|
||||||
|
|
||||||
|
#### IAM Actions (User Management)
|
||||||
|
|
||||||
|
| Action | Description | AWS Aliases |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `iam:list_users` | View all IAM users and their policies | `iam:ListUsers` |
|
||||||
|
| `iam:create_user` | Create new IAM users | `iam:CreateUser` |
|
||||||
| `iam:delete_user` | Delete IAM users | `iam:DeleteUser` |
|
| `iam:delete_user` | Delete IAM users | `iam:DeleteUser` |
|
||||||
| `iam:rotate_key` | Rotate user secrets | `iam:RotateAccessKey` |
|
| `iam:rotate_key` | Rotate user secret keys | `iam:RotateAccessKey` |
|
||||||
| `iam:update_policy` | Modify user policies | `iam:PutUserPolicy` |
|
| `iam:update_policy` | Modify user policies | `iam:PutUserPolicy` |
|
||||||
| `iam:*` | All IAM actions (admin wildcard) | — |
|
| `iam:*` | **Admin wildcard** – grants all IAM actions | — |
|
||||||
|
|
||||||
### Example Policies
|
#### Wildcards
|
||||||
|
|
||||||
|
| Wildcard | Scope | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `*` (in actions) | All S3 actions | Grants `list`, `read`, `write`, `delete`, `share`, `policy`, `lifecycle`, `cors`, `replication` |
|
||||||
|
| `iam:*` | All IAM actions | Grants all `iam:*` actions for user management |
|
||||||
|
| `*` (in bucket) | All buckets | Policy applies to every bucket |
|
||||||
|
|
||||||
|
### IAM Policy Structure
|
||||||
|
|
||||||
|
User policies are stored as a JSON array of policy objects. Each object specifies a bucket and the allowed actions:
|
||||||
|
|
||||||
**Full Control (admin):**
|
|
||||||
```json
|
```json
|
||||||
[{"bucket": "*", "actions": ["list", "read", "write", "delete", "share", "policy", "replication", "iam:*"]}]
|
[
|
||||||
|
{
|
||||||
|
"bucket": "<bucket-name-or-wildcard>",
|
||||||
|
"actions": ["<action1>", "<action2>", ...]
|
||||||
|
}
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
**Read-Only:**
|
**Fields:**
|
||||||
|
- `bucket`: The bucket name (case-insensitive) or `*` for all buckets
|
||||||
|
- `actions`: Array of action strings (simple names or AWS aliases)
|
||||||
|
|
||||||
|
### Example User Policies
|
||||||
|
|
||||||
|
**Full Administrator (complete system access):**
|
||||||
|
```json
|
||||||
|
[{"bucket": "*", "actions": ["list", "read", "write", "delete", "share", "policy", "lifecycle", "cors", "replication", "iam:*"]}]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Read-Only User (browse and download only):**
|
||||||
```json
|
```json
|
||||||
[{"bucket": "*", "actions": ["list", "read"]}]
|
[{"bucket": "*", "actions": ["list", "read"]}]
|
||||||
```
|
```
|
||||||
|
|
||||||
**Single Bucket Access (no listing other buckets):**
|
**Single Bucket Full Access (no access to other buckets):**
|
||||||
```json
|
```json
|
||||||
[{"bucket": "user-bucket", "actions": ["read", "write", "delete"]}]
|
[{"bucket": "user-bucket", "actions": ["list", "read", "write", "delete"]}]
|
||||||
```
|
```
|
||||||
|
|
||||||
**Bucket Access with Replication:**
|
**Multiple Bucket Access (different permissions per bucket):**
|
||||||
```json
|
```json
|
||||||
[{"bucket": "my-bucket", "actions": ["list", "read", "write", "delete", "replication"]}]
|
[
|
||||||
|
{"bucket": "public-data", "actions": ["list", "read"]},
|
||||||
|
{"bucket": "my-uploads", "actions": ["list", "read", "write", "delete"]},
|
||||||
|
{"bucket": "team-shared", "actions": ["list", "read", "write"]}
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**IAM Manager (manage users but no data access):**
|
||||||
|
```json
|
||||||
|
[{"bucket": "*", "actions": ["iam:list_users", "iam:create_user", "iam:delete_user", "iam:rotate_key", "iam:update_policy"]}]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Replication Operator (manage replication only):**
|
||||||
|
```json
|
||||||
|
[{"bucket": "*", "actions": ["list", "read", "replication"]}]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Lifecycle Manager (configure object expiration):**
|
||||||
|
```json
|
||||||
|
[{"bucket": "*", "actions": ["list", "lifecycle"]}]
|
||||||
|
```
|
||||||
|
|
||||||
|
**CORS Administrator (configure cross-origin access):**
|
||||||
|
```json
|
||||||
|
[{"bucket": "*", "actions": ["cors"]}]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Bucket Administrator (full bucket config, no IAM access):**
|
||||||
|
```json
|
||||||
|
[{"bucket": "my-bucket", "actions": ["list", "read", "write", "delete", "policy", "lifecycle", "cors"]}]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Upload-Only User (write but cannot read back):**
|
||||||
|
```json
|
||||||
|
[{"bucket": "drop-box", "actions": ["write"]}]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Backup Operator (read, list, and replicate):**
|
||||||
|
```json
|
||||||
|
[{"bucket": "*", "actions": ["list", "read", "replication"]}]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using AWS-Style Action Names
|
||||||
|
|
||||||
|
You can use AWS S3 action names instead of simple names. They are automatically normalized:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"bucket": "my-bucket",
|
||||||
|
"actions": [
|
||||||
|
"s3:ListBucket",
|
||||||
|
"s3:GetObject",
|
||||||
|
"s3:PutObject",
|
||||||
|
"s3:DeleteObject"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
This is equivalent to:
|
||||||
|
```json
|
||||||
|
[{"bucket": "my-bucket", "actions": ["list", "read", "write", "delete"]}]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Managing Users via API
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List all users (requires iam:list_users)
|
||||||
|
curl http://localhost:5000/iam/users \
|
||||||
|
-H "X-Access-Key: ..." -H "X-Secret-Key: ..."
|
||||||
|
|
||||||
|
# Create a new user (requires iam:create_user)
|
||||||
|
curl -X POST http://localhost:5000/iam/users \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "X-Access-Key: ..." -H "X-Secret-Key: ..." \
|
||||||
|
-d '{
|
||||||
|
"display_name": "New User",
|
||||||
|
"policies": [{"bucket": "*", "actions": ["list", "read"]}]
|
||||||
|
}'
|
||||||
|
|
||||||
|
# Rotate user secret (requires iam:rotate_key)
|
||||||
|
curl -X POST http://localhost:5000/iam/users/<access-key>/rotate \
|
||||||
|
-H "X-Access-Key: ..." -H "X-Secret-Key: ..."
|
||||||
|
|
||||||
|
# Update user policies (requires iam:update_policy)
|
||||||
|
curl -X PUT http://localhost:5000/iam/users/<access-key>/policies \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "X-Access-Key: ..." -H "X-Secret-Key: ..." \
|
||||||
|
-d '[{"bucket": "*", "actions": ["list", "read", "write"]}]'
|
||||||
|
|
||||||
|
# Delete a user (requires iam:delete_user)
|
||||||
|
curl -X DELETE http://localhost:5000/iam/users/<access-key> \
|
||||||
|
-H "X-Access-Key: ..." -H "X-Secret-Key: ..."
|
||||||
|
```
|
||||||
|
|
||||||
|
### Permission Precedence
|
||||||
|
|
||||||
|
When a request is made, permissions are evaluated in this order:
|
||||||
|
|
||||||
|
1. **Authentication** – Verify the access key and secret key are valid
|
||||||
|
2. **Lockout Check** – Ensure the account is not locked due to failed attempts
|
||||||
|
3. **IAM Policy Check** – Verify the user has the required action for the target bucket
|
||||||
|
4. **Bucket Policy Check** – If a bucket policy exists, verify it allows the action
|
||||||
|
|
||||||
|
A request is allowed only if:
|
||||||
|
- The IAM policy grants the action, AND
|
||||||
|
- The bucket policy allows the action (or no bucket policy exists)
|
||||||
|
|
||||||
|
### Common Permission Scenarios
|
||||||
|
|
||||||
|
| Scenario | Required Actions |
|
||||||
|
| --- | --- |
|
||||||
|
| Browse bucket contents | `list` |
|
||||||
|
| Download a file | `read` |
|
||||||
|
| Upload a file | `write` |
|
||||||
|
| Delete a file | `delete` |
|
||||||
|
| Generate presigned URL (GET) | `read` |
|
||||||
|
| Generate presigned URL (PUT) | `write` |
|
||||||
|
| Generate presigned URL (DELETE) | `delete` |
|
||||||
|
| Enable versioning | `write` (includes `s3:PutBucketVersioning`) |
|
||||||
|
| View bucket policy | `policy` |
|
||||||
|
| Modify bucket policy | `policy` |
|
||||||
|
| Configure lifecycle rules | `lifecycle` |
|
||||||
|
| View lifecycle rules | `lifecycle` |
|
||||||
|
| Configure CORS | `cors` |
|
||||||
|
| View CORS rules | `cors` |
|
||||||
|
| Configure replication | `replication` (admin-only for creation) |
|
||||||
|
| Pause/resume replication | `replication` |
|
||||||
|
| Manage other users | `iam:*` or specific `iam:` actions |
|
||||||
|
| Set bucket quotas | `iam:*` or `iam:list_users` (admin feature) |
|
||||||
|
|
||||||
|
### Security Best Practices
|
||||||
|
|
||||||
|
1. **Principle of Least Privilege** – Grant only the permissions users need
|
||||||
|
2. **Avoid Wildcards** – Use specific bucket names instead of `*` when possible
|
||||||
|
3. **Rotate Secrets Regularly** – Use the rotate key feature periodically
|
||||||
|
4. **Separate Admin Accounts** – Don't use admin accounts for daily operations
|
||||||
|
5. **Monitor Failed Logins** – Check logs for repeated authentication failures
|
||||||
|
6. **Use Bucket Policies for Fine-Grained Control** – Combine with IAM for defense in depth
|
||||||
|
|
||||||
## 5. Bucket Policies & Presets
|
## 5. Bucket Policies & Presets
|
||||||
|
|
||||||
- **Storage**: Policies are persisted in `data/.myfsio.sys/config/bucket_policies.json` under `{"policies": {"bucket": {...}}}`.
|
- **Storage**: Policies are persisted in `data/.myfsio.sys/config/bucket_policies.json` under `{"policies": {"bucket": {...}}}`.
|
||||||
|
|||||||
@@ -67,12 +67,14 @@
|
|||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if can_edit_policy %}
|
{% if can_manage_lifecycle %}
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<button class="nav-link {{ 'active' if active_tab == 'lifecycle' else '' }}" id="lifecycle-tab" data-bs-toggle="tab" data-bs-target="#lifecycle-pane" type="button" role="tab" aria-controls="lifecycle-pane" aria-selected="{{ 'true' if active_tab == 'lifecycle' else 'false' }}">
|
<button class="nav-link {{ 'active' if active_tab == 'lifecycle' else '' }}" id="lifecycle-tab" data-bs-toggle="tab" data-bs-target="#lifecycle-pane" type="button" role="tab" aria-controls="lifecycle-pane" aria-selected="{{ 'true' if active_tab == 'lifecycle' else 'false' }}">
|
||||||
Lifecycle
|
Lifecycle
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if can_manage_cors %}
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<button class="nav-link {{ 'active' if active_tab == 'cors' else '' }}" id="cors-tab" data-bs-toggle="tab" data-bs-target="#cors-pane" type="button" role="tab" aria-controls="cors-pane" aria-selected="{{ 'true' if active_tab == 'cors' else 'false' }}">
|
<button class="nav-link {{ 'active' if active_tab == 'cors' else '' }}" id="cors-tab" data-bs-toggle="tab" data-bs-target="#cors-pane" type="button" role="tab" aria-controls="cors-pane" aria-selected="{{ 'true' if active_tab == 'cors' else 'false' }}">
|
||||||
CORS
|
CORS
|
||||||
|
|||||||
Reference in New Issue
Block a user