# yaml-language-server: $schema=./Secretfile.schema.json
# Secretfile.yml
version: '1.0'

# Composable env block that can be overridden at runtime to provide dynamic values for generators and targets. This allows for flexibility in how secrets are generated and where they are stored without needing to change the Secretfile.
variables:
  environment: local
  #aws_account: "222222222222"
  aws_region: us-east-2
  aws_auth: ambient

# Optional metadata about the secrets, such as project name, owner, environments, and compliance requirements. 
metadata:
  project: sentinelone-autodeploy
  owner: platform-security
  environments:
    - prod
  compliance:
    - soc2
    - iso27001

# Providers can be referenced in the secret definitions to specify where the generated secrets should be stored or retrieved from.
# Provider configurations can include authentication details, regions, and other necessary information to connect to the provider's API.
# A provider defined with a name of the provider type (e.g. aws) updates the default configuration for that provider type. 
# This can then be overridden in individual secret target definitions if needed as well (though it is recommended to simply manage it here).
providers:
  aws:
    auth:
#      kind: "{{var.aws_auth or 'ambient'}}"
      # If the provider is not able to retrieve a secret value (e.g. due to connectivity issues), use 'static' as a fallback to ensure that a value is still generated and stored in the target, even if it's not dynamic. This allows for manual secret seed values to be used in cases where dynamic generation is not possible.
      fallback_generator: static
      config:
        region: "{{var.aws_region or 'us-east-1'}}"
      profiles:
        default:
          kind: ambient
        admin:
          kind: assume_role
          config:
            role_arn: arn:aws:iam::123456789012:role/SecretAdmin
        readonly:
          kind: assume_role
          config:
            role_arn: arn:aws:iam::123456789012:role/SecretReadOnly

  vault:
    kind: vault
    fallback_generator: static
    config: {}
    auth:
      profiles:
        # Optional default auth.
        default:
          kind: token
          config:
            token: root
            url: "{{var.vault_url or 'http://localhost:8200'}}"

# Each secret definition includes a name, variables for interpolation, a kind that references a template for generating the secret value, and targets that specify where the secret should be stored.
# The generator for each field in the template can be configured to produce dynamic values, such as random passwords, API keys, or values based on a script or external service. 
# This allows for automated secret generation and rotation.
# Targets specify the provider, kind of storage, and configuration for how the secret should be stored. This can include details like the name of a parameter in AWS SSM, the path to a file, or the connection details for a vault.
# The same secret can have multiple targets, allowing it to be stored in multiple places if needed (e.g. both in AWS SSM and in a local file).
# The lockfile will store a one-way hash of the generated secret values for each target, allowing for verification and rotation without storing the actual secret values. 
# When a secret is generated, the hash of the value is compared to the hash in the lockfile to determine if the secret has changed and needs to be updated in the target.
# If there are multiple targets for a secret, the same generated value will be stored in all targets, and the lockfile will track the hash for each target to ensure consistency across all storage locations.
# At least one target must be able to store the secret value securely; otherwise, the secret generation will fail.
# If one of multiple targets fails to store the secret, the system should have a rollback or synchronization mechanism to ensure that the secret is not left in an inconsistent state across different storage locations.
secrets:
  - name: s1
    # No variables needed for this secret, but if passed in would interpolate into the generator or target config. 
    # Can be used to create multiple secrets from the same template with different values (e.g. different API keys for different environments).
    #vars: {} # Optional
    # Values automatically first try to pull from environment variables based on definition lineage in upper case.
    # For this secret template that would result in the following env vars being queried first:
    #   - S1_API_KEY
    #   - S1_MGMT_URL
    #   - S1_SITE_TOKEN
    #   - S1_VERSION_STATUS
    # If an individual environment variable is not found in the environment, then we use the defined generator.
    # If a generator is not able to produce a value, a fallback will be used. This is often `static` (which is just a default value or human input).
    kind: templates.sentinelone_agent
    one_time: true

  # A locally generated static secret
  - name: root_db_password
    kind: random_password
    # Optional: Indicates that we may generate the secret and seed it but afterwards it is managed by another process and should 
    # not be updated again by this system. This can be useful for secrets that are generated once and then stored in a 
    # vault or other secure location, where they will be retrieved from going forward but not updated by this system.
    one_time: true
    # Rotation period for one_time secrets emit warnings when running rotation but does not actually rotate the secret since it is only generated once. 
    # This is useful for tracking when a secret is due for rotation even if it cannot be automatically rotated by the system.
    rotation_period: 90d
    config:
      length: 32
      special: true
      upper: true
      lower: true
      number: true
      # Exclude characters that may cause issues in certain cases, such as when used in a database connection string or shell command.
      exclude_characters: '"@/\`' 
    targets:
      - provider: local
        # The 'file' kind allows for storing secrets in local files, which can be useful for development or for applications that read secrets from files.
        # Kind can be 'file' for standard files or 'template' for more complex file generation (e.g. config files with multiple secrets).
        kind: file
        config:
          path: .env.local
          # Options for the file format include: dotenv, json, yaml, and toml.
          format: dotenv # The variable name will be derived from the secret name in upper case (e.g. ROOT_DB_PASSWORD).
          # Intelligently append/overwrite to the file rather than overwrite.
          merge: true 

templates:
  sentinelone_agent:
    description: SentinelOne Agent AutoDeploy secrets
    fields:
      # Each field in the template represents a component of the overall secret, which can be generated and stored separately if needed.
      # For example, an API key might be stored in AWS SSM, while a management URL might be stored in a local file.
      api_key:
        description: SentinelOne API Key
        generator:
          kind: static
          config:
            # Optional regex validation pattern to ensure that the generated value meets the expected format 
            # for a SentinelOne API key (e.g. starts with "ey" and ends with "=="). 
            # This helps catch configuration errors early and ensures that any manually provided values also meet the required format.
            validation: ^[a-zA-Z0-9\.\-].*$
            # Optional default value for the API key, which can be used if no value is provided through environment variables or the generator.
            #default: ey1234567890abcdef==
            # Optional rotation period for the secret, which can be used to trigger automatic regeneration of the secret
            # after a certain amount of time (e.g. 90 days). This helps ensure that secrets are regularly updated to reduce the risk of compromise, 
            # and can be used in conjunction with the lockfile to manage secret rotation without storing actual secret values.
            rotation_period: 90d
        # Per-field targets are in addition to any top-level targets defined for the secret
        # and will override \the top-level targets if there is a conflict in configuration. 
        # This allows for granular control over where specific fields of a secret are stored, 
        # which can be useful for cases where different fields have different sensitivity levels 
        # or need to be stored in different locations.
        targets:
          - provider: aws
            kind: ssm_parameter
            config:
              name: /s1-autodeploy/api-key
              type: SecureString
              overwrite: true
      mgmt_url:
        description: SentinelOne Management URL
        generator:
          kind: static
          config:
            validation: ^https:\/\/.*\/$
            default: https://usea1-cyberiant-001.sentinelone.net/
        targets:
          - provider: aws
            profile: admin # Use the 'admin' auth profile defined in the AWS provider configuration, which assumes a role with permissions to manage secrets.
            kind: ssm_parameter
            config:
              name: /s1-autodeploy/mgmt-url
              type: SecureString
              overwrite: true
      site_token:
        description: SentinelOne Site Token (ie. ey1234568*==)
        generator:
          kind: static
          config:
            validation: ^.*==$
        targets:
          - provider: aws
            kind: ssm_parameter
            config:
              name: /s1-autodeploy/site-token
              type: SecureString
              overwrite: true
      version_status:
          description: SentinelOne Version Status, ge or ea
          generator:
            kind: static
            config:
              validation: ^(ge|ea)$
              default: ge
          targets:
            - provider: aws
              kind: ssm_parameter
              config:
                name: /s1-autodeploy/version-status
                type: String
                overwrite: true
    # Targets defined at the template level apply to all fields in the template, unless overridden by field-level targets.
    targets:
      - provider: local
        kind: file
        config:
          path: sentinelone.json
          # Format the secrets as JSON in a file, which can be easily parsed by applications. 
          # The keys will be derived from the field names (e.g. api_key, mgmt_url, site_token, version_status).
          format: json

# Optional (Reserved for future use)
policies: {}
labels: {}
annotations: {}
