feat(ProviderSecret): PRWLR-5128 Implement role assumption provider credentials for AWS (#64)

* feat(ProviderSecret): PRWLR-5128 add role assumption logic

* feat(Provider): PRWLR-5128 add static kwargs to Prowler provider initialization

* chore: PRWLR-5128 update API spec

* test(ProviderSecret): PRWLR-5128 add new unit tests

* chore: PRWLR-5128 update API spec
This commit is contained in:
Víctor Fernández Poyatos
2024-10-31 16:25:20 +01:00
committed by GitHub
parent c188028de5
commit fc19fbac68
7 changed files with 806 additions and 99 deletions
+1
View File
@@ -626,6 +626,7 @@ class ResourceFindingMapping(PostgresPartitionedModel, RowLevelSecurityProtected
class ProviderSecret(RowLevelSecurityProtectedModel):
class TypeChoices(models.TextChoices):
STATIC = "static", _("Key-value pairs")
ROLE = "role", _("Role assumption")
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
inserted_at = models.DateTimeField(auto_now_add=True, editable=False)
+449 -50
View File
@@ -11,12 +11,11 @@ paths:
get:
operationId: findings_list
description: Retrieve a list of all findings with options for filtering by various
criteria. If no `scan` or `inserted_at` filter is provided, a default filter
will be applied that will return all findings for the current day.
criteria.
summary: List all findings
parameters:
- in: query
name: fields[Findings]
name: fields[Finding]
schema:
type: array
items:
@@ -452,7 +451,7 @@ paths:
summary: Retrieve data from a specific finding
parameters:
- in: query
name: fields[Findings]
name: fields[Finding]
schema:
type: array
items:
@@ -859,6 +858,7 @@ paths:
- inserted_at
- updated_at
- name
- secret_type
- provider
- url
description: endpoint return only specific fields in the response on a per-type
@@ -976,6 +976,7 @@ paths:
- inserted_at
- updated_at
- name
- secret_type
- provider
- url
description: endpoint return only specific fields in the response on a per-type
@@ -1290,6 +1291,7 @@ paths:
type: string
enum:
- findings
- provider
description: include query parameter to allow the client to customize which
related resources should be returned.
explode: false
@@ -1386,6 +1388,7 @@ paths:
type: string
enum:
- findings
- provider
description: include query parameter to allow the client to customize which
related resources should be returned.
explode: false
@@ -2609,7 +2612,7 @@ components:
properties:
uid:
type: string
maxLength: 150
maxLength: 300
delta:
enum:
- new
@@ -2698,25 +2701,30 @@ components:
type: object
properties:
data:
type: object
properties:
id:
type: string
type:
type: string
enum:
- Resource
title: Resource Type Name
description: The [type](https://jsonapi.org/format/#document-resource-object-identification)
member is used to describe resource objects that share common
attributes and relationships.
required:
- id
- type
type: array
items:
type: object
properties:
id:
type: string
format: uuid
title: Resource Identifier
description: The identifier of the related object.
type:
type: string
enum:
- Resource
title: Resource Type Name
description: The [type](https://jsonapi.org/format/#document-resource-object-identification)
member is used to describe resource objects that share common
attributes and relationships.
required:
- id
- type
required:
- data
description: The identifier of the related object.
title: Resource Identifier
description: A related resource object from type Resource
title: Resource
readOnly: true
required:
- scan
@@ -2730,7 +2738,7 @@ components:
FindingTypeEnum:
type: string
enum:
- Findings
- Finding
Membership:
type: object
required:
@@ -2941,7 +2949,118 @@ components:
nullable: true
maxLength: 100
minLength: 3
secret_type:
enum:
- static
- role
type: string
description: |-
* `static` - Key-value pairs
* `role` - Role assumption
readOnly: true
secret:
oneOf:
- type: object
title: AWS Static Credentials
properties:
aws_access_key_id:
type: string
description: The AWS access key ID. Required for environments
where no IAM role is being assumed and direct AWS access
is needed.
aws_secret_access_key:
type: string
description: The AWS secret access key. Must accompany 'aws_access_key_id'
to authorize access to AWS resources.
aws_session_token:
type: string
description: The session token associated with temporary credentials.
Only needed for session-based or temporary AWS access.
required:
- aws_access_key_id
- aws_secret_access_key
- type: object
title: AWS Assume Role
properties:
role_arn:
type: string
description: The Amazon Resource Name (ARN) of the role to
assume. Required for AWS role assumption.
aws_access_key_id:
type: string
description: The AWS access key ID. Only required if the environment
lacks pre-configured AWS credentials.
aws_secret_access_key:
type: string
description: The AWS secret access key. Required if 'aws_access_key_id'
is provided or if no AWS credentials are pre-configured.
aws_session_token:
type: string
description: The session token for temporary credentials,
if applicable.
session_duration:
type: integer
minimum: 900
maximum: 43200
default: 3600
description: The duration (in seconds) for the role session.
external_id:
type: string
description: An optional identifier to enhance security for
role assumption; may be required by the role administrator.
role_session_name:
type: string
description: An identifier for the role session, useful for
tracking sessions in AWS logs.
required:
- role_arn
- type: object
title: Azure Static Credentials
properties:
client_id:
type: string
description: The Azure application (client) ID for authentication
in Azure AD.
client_secret:
type: string
description: The client secret associated with the application
(client) ID, providing secure access.
tenant_id:
type: string
description: The Azure tenant ID, representing the directory
where the application is registered.
required:
- client_id
- client_secret
- tenant_id
- type: object
title: GCP Static Credentials
properties:
client_id:
type: string
description: The client ID from Google Cloud, used to identify
the application for GCP access.
client_secret:
type: string
description: The client secret associated with the GCP client
ID, required for secure access.
refresh_token:
type: string
description: A refresh token that allows the application to
obtain new access tokens for extended use.
required:
- client_id
- client_secret
- refresh_token
- type: object
title: Kubernetes Static Credentials
properties:
kubeconfig_content:
type: string
description: The content of the Kubernetes kubeconfig file,
encoded as a string.
required:
- kubeconfig_content
writeOnly: true
required:
- secret
@@ -3132,6 +3251,7 @@ components:
type: string
format: email
minLength: 1
description: Case insensitive
maxLength: 254
company_name:
type: string
@@ -3364,6 +3484,16 @@ components:
nullable: true
maxLength: 100
minLength: 3
secret_type:
enum:
- static
- role
type: string
description: |-
* `static` - Key-value pairs
* `role` - Role assumption
required:
- secret_type
relationships:
type: object
properties:
@@ -3424,49 +3554,113 @@ components:
secret_type:
enum:
- static
- role
type: string
description: '* `static` - Key-value pairs'
description: |-
* `static` - Key-value pairs
* `role` - Role assumption
secret:
oneOf:
- type: object
title: AWS static credentials
title: AWS Static Credentials
properties:
aws_access_key_id:
type: string
description: The AWS access key ID. Required for environments
where no IAM role is being assumed and direct AWS access is
needed.
aws_secret_access_key:
type: string
description: The AWS secret access key. Must accompany 'aws_access_key_id'
to authorize access to AWS resources.
aws_session_token:
type: string
description: The session token associated with temporary credentials.
Only needed for session-based or temporary AWS access.
required:
- aws_access_key_id
- aws_secret_access_key
- type: object
title: Azure static credentials
title: AWS Assume Role
properties:
role_arn:
type: string
description: The Amazon Resource Name (ARN) of the role to assume.
Required for AWS role assumption.
aws_access_key_id:
type: string
description: The AWS access key ID. Only required if the environment
lacks pre-configured AWS credentials.
aws_secret_access_key:
type: string
description: The AWS secret access key. Required if 'aws_access_key_id'
is provided or if no AWS credentials are pre-configured.
aws_session_token:
type: string
description: The session token for temporary credentials, if applicable.
session_duration:
type: integer
minimum: 900
maximum: 43200
default: 3600
description: The duration (in seconds) for the role session.
external_id:
type: string
description: An optional identifier to enhance security for role
assumption; may be required by the role administrator.
role_session_name:
type: string
description: An identifier for the role session, useful for tracking
sessions in AWS logs.
required:
- role_arn
- type: object
title: Azure Static Credentials
properties:
client_id:
type: string
description: The Azure application (client) ID for authentication
in Azure AD.
client_secret:
type: string
description: The client secret associated with the application
(client) ID, providing secure access.
tenant_id:
type: string
description: Azure tenant ID
description: The Azure tenant ID, representing the directory where
the application is registered.
required:
- client_id
- client_secret
- tenant_id
- type: object
title: GCP static credentials
title: GCP Static Credentials
properties:
client_id:
type: string
description: The client ID from Google Cloud, used to identify
the application for GCP access.
client_secret:
type: string
description: The client secret associated with the GCP client
ID, required for secure access.
refresh_token:
type: string
description: A refresh token that allows the application to obtain
new access tokens for extended use.
required:
- client_id
- client_secret
- refresh_token
- type: object
title: Kubernetes Static Credentials
properties:
kubeconfig_content:
type: string
description: The content of the Kubernetes kubeconfig file, encoded
as a string.
required:
- kubeconfig_content
writeOnly: true
required:
- secret_type
@@ -3535,49 +3729,114 @@ components:
secret_type:
enum:
- static
- role
type: string
description: '* `static` - Key-value pairs'
description: |-
* `static` - Key-value pairs
* `role` - Role assumption
secret:
oneOf:
- type: object
title: AWS static credentials
title: AWS Static Credentials
properties:
aws_access_key_id:
type: string
description: The AWS access key ID. Required for environments
where no IAM role is being assumed and direct AWS access
is needed.
aws_secret_access_key:
type: string
description: The AWS secret access key. Must accompany 'aws_access_key_id'
to authorize access to AWS resources.
aws_session_token:
type: string
description: The session token associated with temporary credentials.
Only needed for session-based or temporary AWS access.
required:
- aws_access_key_id
- aws_secret_access_key
- type: object
title: Azure static credentials
title: AWS Assume Role
properties:
role_arn:
type: string
description: The Amazon Resource Name (ARN) of the role to
assume. Required for AWS role assumption.
aws_access_key_id:
type: string
description: The AWS access key ID. Only required if the environment
lacks pre-configured AWS credentials.
aws_secret_access_key:
type: string
description: The AWS secret access key. Required if 'aws_access_key_id'
is provided or if no AWS credentials are pre-configured.
aws_session_token:
type: string
description: The session token for temporary credentials,
if applicable.
session_duration:
type: integer
minimum: 900
maximum: 43200
default: 3600
description: The duration (in seconds) for the role session.
external_id:
type: string
description: An optional identifier to enhance security for
role assumption; may be required by the role administrator.
role_session_name:
type: string
description: An identifier for the role session, useful for
tracking sessions in AWS logs.
required:
- role_arn
- type: object
title: Azure Static Credentials
properties:
client_id:
type: string
description: The Azure application (client) ID for authentication
in Azure AD.
client_secret:
type: string
description: The client secret associated with the application
(client) ID, providing secure access.
tenant_id:
type: string
description: Azure tenant ID
description: The Azure tenant ID, representing the directory
where the application is registered.
required:
- client_id
- client_secret
- tenant_id
- type: object
title: GCP static credentials
title: GCP Static Credentials
properties:
client_id:
type: string
description: The client ID from Google Cloud, used to identify
the application for GCP access.
client_secret:
type: string
description: The client secret associated with the GCP client
ID, required for secure access.
refresh_token:
type: string
description: A refresh token that allows the application to
obtain new access tokens for extended use.
required:
- client_id
- client_secret
- refresh_token
- type: object
title: Kubernetes Static Credentials
properties:
kubeconfig_content:
type: string
description: The content of the Kubernetes kubeconfig file,
encoded as a string.
required:
- kubeconfig_content
writeOnly: true
required:
- secret_type
@@ -3657,7 +3916,117 @@ components:
nullable: true
maxLength: 100
minLength: 3
secret_type:
enum:
- static
- role
type: string
description: |-
* `static` - Key-value pairs
* `role` - Role assumption
readOnly: true
secret:
oneOf:
- type: object
title: AWS Static Credentials
properties:
aws_access_key_id:
type: string
description: The AWS access key ID. Required for environments
where no IAM role is being assumed and direct AWS access is
needed.
aws_secret_access_key:
type: string
description: The AWS secret access key. Must accompany 'aws_access_key_id'
to authorize access to AWS resources.
aws_session_token:
type: string
description: The session token associated with temporary credentials.
Only needed for session-based or temporary AWS access.
required:
- aws_access_key_id
- aws_secret_access_key
- type: object
title: AWS Assume Role
properties:
role_arn:
type: string
description: The Amazon Resource Name (ARN) of the role to assume.
Required for AWS role assumption.
aws_access_key_id:
type: string
description: The AWS access key ID. Only required if the environment
lacks pre-configured AWS credentials.
aws_secret_access_key:
type: string
description: The AWS secret access key. Required if 'aws_access_key_id'
is provided or if no AWS credentials are pre-configured.
aws_session_token:
type: string
description: The session token for temporary credentials, if applicable.
session_duration:
type: integer
minimum: 900
maximum: 43200
default: 3600
description: The duration (in seconds) for the role session.
external_id:
type: string
description: An optional identifier to enhance security for role
assumption; may be required by the role administrator.
role_session_name:
type: string
description: An identifier for the role session, useful for tracking
sessions in AWS logs.
required:
- role_arn
- type: object
title: Azure Static Credentials
properties:
client_id:
type: string
description: The Azure application (client) ID for authentication
in Azure AD.
client_secret:
type: string
description: The client secret associated with the application
(client) ID, providing secure access.
tenant_id:
type: string
description: The Azure tenant ID, representing the directory where
the application is registered.
required:
- client_id
- client_secret
- tenant_id
- type: object
title: GCP Static Credentials
properties:
client_id:
type: string
description: The client ID from Google Cloud, used to identify
the application for GCP access.
client_secret:
type: string
description: The client secret associated with the GCP client
ID, required for secure access.
refresh_token:
type: string
description: A refresh token that allows the application to obtain
new access tokens for extended use.
required:
- client_id
- client_secret
- refresh_token
- type: object
title: Kubernetes Static Credentials
properties:
kubeconfig_content:
type: string
description: The content of the Kubernetes kubeconfig file, encoded
as a string.
required:
- kubeconfig_content
writeOnly: true
required:
- secret
@@ -3781,25 +4150,30 @@ components:
type: object
properties:
data:
type: object
properties:
id:
type: string
type:
type: string
enum:
- Finding
title: Resource Type Name
description: The [type](https://jsonapi.org/format/#document-resource-object-identification)
member is used to describe resource objects that share common
attributes and relationships.
required:
- id
- type
type: array
items:
type: object
properties:
id:
type: string
format: uuid
title: Resource Identifier
description: The identifier of the related object.
type:
type: string
enum:
- Finding
title: Resource Type Name
description: The [type](https://jsonapi.org/format/#document-resource-object-identification)
member is used to describe resource objects that share common
attributes and relationships.
required:
- id
- type
required:
- data
description: The identifier of the related object.
title: Resource Identifier
description: A related resource object from type Finding
title: Finding
readOnly: true
required:
- provider
@@ -4424,6 +4798,7 @@ components:
email:
type: string
format: email
description: Case insensitive
maxLength: 254
company_name:
type: string
@@ -4492,6 +4867,7 @@ components:
email:
type: string
format: email
description: Case insensitive
maxLength: 254
company_name:
type: string
@@ -4530,6 +4906,7 @@ components:
type: string
format: email
minLength: 1
description: Case insensitive
maxLength: 254
company_name:
type: string
@@ -4583,6 +4960,7 @@ components:
email:
type: string
format: email
description: Case insensitive
maxLength: 254
company_name:
type: string
@@ -4602,3 +4980,24 @@ components:
type: http
scheme: bearer
bearerFormat: JWT
tags:
- name: User
description: Endpoints for managing user accounts.
- name: Token
description: Endpoints for token management, including obtaining a new token and
refreshing an existing token for authentication purposes.
- name: Tenant
description: Endpoints for managing tenants, along with their memberships.
- name: Provider
description: Endpoints for managing providers (AWS, GCP, Azure, etc...).
- name: Scan
description: Endpoints for triggering manual scans and viewing scan results.
- name: Resource
description: Endpoints for managing resources discovered by scans, allowing retrieval
and filtering of resource information.
- name: Finding
description: Endpoints for managing findings, allowing retrieval and filtering of
findings that result from scans.
- name: Task
description: Endpoints for task management, allowing retrieval of task status and
revoking tasks that have not started.
+73
View File
@@ -12,6 +12,7 @@ from api.utils import (
return_prowler_provider,
initialize_prowler_provider,
prowler_provider_connection_test,
get_prowler_provider_kwargs,
)
@@ -136,3 +137,75 @@ class TestProwlerProviderConnectionTest:
mock_return_prowler_provider.return_value.test_connection.assert_called_once_with(
key="value", provider_id="1234567890", raise_on_exception=False
)
class TestGetProwlerProviderKwargs:
@pytest.mark.parametrize(
"provider_type, expected_extra_kwargs",
[
(
Provider.ProviderChoices.AWS.value,
{},
),
(
Provider.ProviderChoices.AZURE.value,
{"subscription_ids": ["provider_uid"]},
),
(
Provider.ProviderChoices.GCP.value,
{"project_ids": ["provider_uid"]},
),
(
Provider.ProviderChoices.KUBERNETES.value,
{"context": "provider_uid"},
),
],
)
def test_get_prowler_provider_kwargs(self, provider_type, expected_extra_kwargs):
provider_uid = "provider_uid"
secret_dict = {"key": "value"}
secret_mock = MagicMock()
secret_mock.secret = secret_dict
provider = MagicMock()
provider.provider = provider_type
provider.secret = secret_mock
provider.uid = provider_uid
result = get_prowler_provider_kwargs(provider)
expected_result = {**secret_dict, **expected_extra_kwargs}
assert result == expected_result
def test_get_prowler_provider_kwargs_unsupported_provider(self):
# Setup
provider_uid = "provider_uid"
secret_dict = {"key": "value"}
secret_mock = MagicMock()
secret_mock.secret = secret_dict
provider = MagicMock()
provider.provider = "UNSUPPORTED_PROVIDER"
provider.secret = secret_mock
provider.uid = provider_uid
result = get_prowler_provider_kwargs(provider)
expected_result = secret_dict.copy()
assert result == expected_result
def test_get_prowler_provider_kwargs_no_secret(self):
# Setup
provider_uid = "provider_uid"
secret_mock = MagicMock()
secret_mock.secret = {}
provider = MagicMock()
provider.provider = Provider.ProviderChoices.AWS.value
provider.secret = secret_mock
provider.uid = provider_uid
result = get_prowler_provider_kwargs(provider)
expected_result = {}
assert result == expected_result
+73 -14
View File
@@ -1139,21 +1139,82 @@ class TestProviderSecretViewSet:
)
assert response.status_code == status.HTTP_404_NOT_FOUND
@pytest.mark.parametrize(
"provider_type, secret_type, secret_data",
[
# AWS with STATIC secret
(
Provider.ProviderChoices.AWS.value,
ProviderSecret.TypeChoices.STATIC,
{
"aws_access_key_id": "value",
"aws_secret_access_key": "value",
"aws_session_token": "value",
},
),
# AWS with ROLE secret
(
Provider.ProviderChoices.AWS.value,
ProviderSecret.TypeChoices.ROLE,
{
"role_arn": "arn:aws:iam::123456789012:role/example-role",
# Optional fields
"external_id": "external-id",
"role_session_name": "session-name",
"session_duration": 3600,
"aws_access_key_id": "value",
"aws_secret_access_key": "value",
"aws_session_token": "value",
},
),
# Azure with STATIC secret
(
Provider.ProviderChoices.AZURE.value,
ProviderSecret.TypeChoices.STATIC,
{
"client_id": "client-id",
"client_secret": "client-secret",
"tenant_id": "tenant-id",
},
),
# GCP with STATIC secret
(
Provider.ProviderChoices.GCP.value,
ProviderSecret.TypeChoices.STATIC,
{
"client_id": "client-id",
"client_secret": "client-secret",
"refresh_token": "refresh-token",
},
),
# Kubernetes with STATIC secret
(
Provider.ProviderChoices.KUBERNETES.value,
ProviderSecret.TypeChoices.STATIC,
{
"kubeconfig_content": "kubeconfig-content",
},
),
],
)
def test_provider_secrets_create_valid(
self, authenticated_client, providers_fixture
self,
authenticated_client,
providers_fixture,
provider_type,
secret_type,
secret_data,
):
provider, *_ = providers_fixture
# Get the provider from the fixture and set its type
provider = Provider.objects.filter(provider=provider_type)[0]
data = {
"data": {
"type": "ProviderSecret",
"attributes": {
"name": "My Secret",
"secret_type": "static",
"secret": {
"aws_access_key_id": "value",
"aws_secret_access_key": "value",
"aws_session_token": "value",
},
"secret_type": secret_type,
"secret": secret_data,
},
"relationships": {
"provider": {"data": {"type": "Provider", "id": str(provider.id)}}
@@ -1167,13 +1228,11 @@ class TestProviderSecretViewSet:
)
assert response.status_code == status.HTTP_201_CREATED
assert ProviderSecret.objects.count() == 1
assert ProviderSecret.objects.get().name == data["data"]["attributes"]["name"]
provider_secret = ProviderSecret.objects.first()
assert provider_secret.name == data["data"]["attributes"]["name"]
assert provider_secret.secret_type == data["data"]["attributes"]["secret_type"]
assert (
ProviderSecret.objects.get().secret_type
== data["data"]["attributes"]["secret_type"]
)
assert (
str(ProviderSecret.objects.get().provider.id)
str(provider_secret.provider.id)
== data["data"]["relationships"]["provider"]["data"]["id"]
)
+27 -2
View File
@@ -62,6 +62,31 @@ def return_prowler_provider(
return prowler_provider
def get_prowler_provider_kwargs(provider: Provider) -> dict:
"""Get the Prowler provider kwargs based on the given provider type.
Args:
provider (Provider): The provider object containing the provider type and associated secret.
Returns:
dict: The provider kwargs for the corresponding provider class.
"""
prowler_provider_kwargs = provider.secret.secret
if provider.provider == Provider.ProviderChoices.AZURE.value:
prowler_provider_kwargs = {
**prowler_provider_kwargs,
"subscription_ids": [provider.uid],
}
elif provider.provider == Provider.ProviderChoices.GCP.value:
prowler_provider_kwargs = {
**prowler_provider_kwargs,
"project_ids": [provider.uid],
}
elif provider.provider == Provider.ProviderChoices.KUBERNETES.value:
prowler_provider_kwargs = {**prowler_provider_kwargs, "context": provider.uid}
return prowler_provider_kwargs
def initialize_prowler_provider(
provider: Provider,
) -> AwsProvider | AzureProvider | GcpProvider | KubernetesProvider:
@@ -76,7 +101,7 @@ def initialize_prowler_provider(
provider's secrets.
"""
prowler_provider = return_prowler_provider(provider)
prowler_provider_kwargs = provider.secret.secret
prowler_provider_kwargs = get_prowler_provider_kwargs(provider)
return prowler_provider(**prowler_provider_kwargs)
@@ -90,7 +115,7 @@ def prowler_provider_connection_test(provider: Provider) -> Connection:
Connection: A connection object representing the result of the connection test for the specified provider.
"""
prowler_provider = return_prowler_provider(provider)
prowler_provider_kwargs = provider.secret.secret
prowler_provider_kwargs = get_prowler_provider_kwargs(provider)
return prowler_provider.test_connection(
**prowler_provider_kwargs, provider_id=provider.uid, raise_on_exception=False
)
+148 -33
View File
@@ -611,16 +611,27 @@ class FindingSerializer(RLSSerializer):
# Provider secrets
class BaseWriteProviderSecretSerializer(BaseWriteSerializer):
@staticmethod
def validate_secret_based_on_provider(provider_type: str, secret: dict):
if provider_type == Provider.ProviderChoices.AWS.value:
serializer = AwsProviderSecret(data=secret)
elif provider_type == Provider.ProviderChoices.AZURE.value:
serializer = AzureProviderSecret(data=secret)
elif provider_type == Provider.ProviderChoices.GCP.value:
serializer = GCPProviderSecret(data=secret)
def validate_secret_based_on_provider(
provider_type: str, secret_type: ProviderSecret.TypeChoices, secret: dict
):
if secret_type == ProviderSecret.TypeChoices.STATIC:
if provider_type == Provider.ProviderChoices.AWS.value:
serializer = AwsProviderSecret(data=secret)
elif provider_type == Provider.ProviderChoices.AZURE.value:
serializer = AzureProviderSecret(data=secret)
elif provider_type == Provider.ProviderChoices.GCP.value:
serializer = GCPProviderSecret(data=secret)
elif provider_type == Provider.ProviderChoices.KUBERNETES.value:
serializer = KubernetesProviderSecret(data=secret)
else:
raise serializers.ValidationError(
{"provider": f"Provider type not supported {provider_type}"}
)
elif secret_type == ProviderSecret.TypeChoices.ROLE:
serializer = AWSRoleAssumptionProviderSecret(data=secret)
else:
raise serializers.ValidationError(
{"provider": f"Provider type not supported {provider_type}"}
{"secret_type": f"Secret type not supported: {secret_type}"}
)
try:
serializer.is_valid(raise_exception=True)
@@ -660,46 +671,148 @@ class GCPProviderSecret(serializers.Serializer):
resource_name = "ProviderSecret"
class KubernetesProviderSecret(serializers.Serializer):
kubeconfig_content = serializers.CharField()
class Meta:
resource_name = "ProviderSecret"
class AWSRoleAssumptionProviderSecret(serializers.Serializer):
role_arn = serializers.CharField()
external_id = serializers.CharField(required=False)
role_session_name = serializers.CharField(required=False)
session_duration = serializers.IntegerField(
required=False, min_value=900, max_value=43200
)
aws_access_key_id = serializers.CharField(required=False)
aws_secret_access_key = serializers.CharField(required=False)
aws_session_token = serializers.CharField(required=False)
class Meta:
resource_name = "ProviderSecret"
@extend_schema_field(
{
"oneOf": [
{
"type": "object",
"title": "AWS static credentials",
"title": "AWS Static Credentials",
"properties": {
"aws_access_key_id": {"type": "string"},
"aws_secret_access_key": {"type": "string"},
"aws_session_token": {"type": "string"},
"aws_access_key_id": {
"type": "string",
"description": "The AWS access key ID. Required for environments where no IAM role is being "
"assumed and direct AWS access is needed.",
},
"aws_secret_access_key": {
"type": "string",
"description": "The AWS secret access key. Must accompany 'aws_access_key_id' to authorize "
"access to AWS resources.",
},
"aws_session_token": {
"type": "string",
"description": "The session token associated with temporary credentials. Only needed for "
"session-based or temporary AWS access.",
},
},
"required": ["aws_access_key_id", "aws_secret_access_key"],
},
{
"type": "object",
"title": "Azure static credentials",
"title": "AWS Assume Role",
"properties": {
"client_id": {"type": "string"},
"client_secret": {"type": "string"},
"tenant_id": {"type": "string", "description": "Azure tenant ID"},
"role_arn": {
"type": "string",
"description": "The Amazon Resource Name (ARN) of the role to assume. Required for AWS role "
"assumption.",
},
"aws_access_key_id": {
"type": "string",
"description": "The AWS access key ID. Only required if the environment lacks pre-configured "
"AWS credentials.",
},
"aws_secret_access_key": {
"type": "string",
"description": "The AWS secret access key. Required if 'aws_access_key_id' is provided or if "
"no AWS credentials are pre-configured.",
},
"aws_session_token": {
"type": "string",
"description": "The session token for temporary credentials, if applicable.",
},
"session_duration": {
"type": "integer",
"minimum": 900,
"maximum": 43200,
"default": 3600,
"description": "The duration (in seconds) for the role session.",
},
"external_id": {
"type": "string",
"description": "An optional identifier to enhance security for role assumption; may be "
"required by the role administrator.",
},
"role_session_name": {
"type": "string",
"description": "An identifier for the role session, useful for tracking sessions in AWS logs.",
},
},
"required": [
"client_id",
"client_secret",
"tenant_id",
],
"required": ["role_arn"],
},
{
"type": "object",
"title": "GCP static credentials",
"title": "Azure Static Credentials",
"properties": {
"client_id": {"type": "string"},
"client_secret": {"type": "string"},
"refresh_token": {"type": "string"},
"client_id": {
"type": "string",
"description": "The Azure application (client) ID for authentication in Azure AD.",
},
"client_secret": {
"type": "string",
"description": "The client secret associated with the application (client) ID, providing "
"secure access.",
},
"tenant_id": {
"type": "string",
"description": "The Azure tenant ID, representing the directory where the application is "
"registered.",
},
},
"required": [
"client_id",
"client_secret",
"refresh_token",
],
"required": ["client_id", "client_secret", "tenant_id"],
},
{
"type": "object",
"title": "GCP Static Credentials",
"properties": {
"client_id": {
"type": "string",
"description": "The client ID from Google Cloud, used to identify the application for GCP "
"access.",
},
"client_secret": {
"type": "string",
"description": "The client secret associated with the GCP client ID, required for secure "
"access.",
},
"refresh_token": {
"type": "string",
"description": "A refresh token that allows the application to obtain new access tokens for "
"extended use.",
},
},
"required": ["client_id", "client_secret", "refresh_token"],
},
{
"type": "object",
"title": "Kubernetes Static Credentials",
"properties": {
"kubeconfig_content": {
"type": "string",
"description": "The content of the Kubernetes kubeconfig file, encoded as a string.",
}
},
"required": ["kubeconfig_content"],
},
]
}
@@ -746,15 +859,16 @@ class ProviderSecretCreateSerializer(RLSSerializer, BaseWriteProviderSecretSeria
def validate(self, attrs):
provider = attrs.get("provider")
secret_type = attrs.get("secret_type")
secret = attrs.get("secret")
validated_attrs = super().validate(attrs)
self.validate_secret_based_on_provider(provider.provider, secret)
self.validate_secret_based_on_provider(provider.provider, secret_type, secret)
return validated_attrs
class ProviderSecretUpdateSerializer(BaseWriteProviderSecretSerializer):
secret = serializers.JSONField(write_only=True)
secret = ProviderSecretField(write_only=True)
class Meta:
model = ProviderSecret
@@ -775,8 +889,9 @@ class ProviderSecretUpdateSerializer(BaseWriteProviderSecretSerializer):
def validate(self, attrs):
provider = self.instance.provider
secret_type = self.instance.secret_type
secret = attrs.get("secret")
validated_attrs = super().validate(attrs)
self.validate_secret_based_on_provider(provider.provider, secret)
self.validate_secret_based_on_provider(provider.provider, secret_type, secret)
return validated_attrs
+35
View File
@@ -139,6 +139,41 @@ class SchemaView(SpectacularAPIView):
spectacular_settings.DESCRIPTION = (
"Prowler API specification.\n\nThis file is auto-generated."
)
spectacular_settings.TAGS = [
{"name": "User", "description": "Endpoints for managing user accounts."},
{
"name": "Token",
"description": "Endpoints for token management, including obtaining a new token and "
"refreshing an existing token for authentication purposes.",
},
{
"name": "Tenant",
"description": "Endpoints for managing tenants, along with their memberships.",
},
{
"name": "Provider",
"description": "Endpoints for managing providers (AWS, GCP, Azure, etc...).",
},
{
"name": "Scan",
"description": "Endpoints for triggering manual scans and viewing scan results.",
},
{
"name": "Resource",
"description": "Endpoints for managing resources discovered by scans, allowing "
"retrieval and filtering of resource information.",
},
{
"name": "Finding",
"description": "Endpoints for managing findings, allowing retrieval and filtering of "
"findings that result from scans.",
},
{
"name": "Task",
"description": "Endpoints for task management, allowing retrieval of task status and "
"revoking tasks that have not started.",
},
]
return super().get(request, *args, **kwargs)