Plugin-framework provider covering projects, environments, applications, Compose stacks, managed databases, domains, mounts, ports, redirects, basic auth, registries, SSH keys, certificates and backup destinations, over Dokploy's tRPC-over-REST API. The shim package exposes the provider to other Go modules, which is how pulumi-dokploy bridges it.
165 lines
4.8 KiB
Markdown
165 lines
4.8 KiB
Markdown
---
|
|
name: terraform-style-guide-security
|
|
description: Generate Terraform HCL code following HashiCorp's security practices
|
|
---
|
|
|
|
# Terraform Style Guide - Security
|
|
|
|
When generating code, apply security hardening:
|
|
|
|
- Enable encryption at rest by default
|
|
- Configure private networking where applicable
|
|
- Apply principle of least privilege for security groups
|
|
- Enable logging and monitoring
|
|
- Never hardcode credentials or secrets
|
|
- Mark sensitive outputs with `sensitive = true`
|
|
- Use `ephemeral` resources and write-only attributes
|
|
for sensitive data when possible
|
|
|
|
## Example: Secure S3 Bucket
|
|
|
|
```hcl
|
|
resource "aws_s3_bucket" "data" {
|
|
bucket = "${var.project}-${var.environment}-data"
|
|
tags = local.common_tags
|
|
}
|
|
|
|
resource "aws_s3_bucket_versioning" "data" {
|
|
bucket = aws_s3_bucket.data.id
|
|
|
|
versioning_configuration {
|
|
status = "Enabled"
|
|
}
|
|
}
|
|
|
|
resource "aws_s3_bucket_server_side_encryption_configuration" "data" {
|
|
bucket = aws_s3_bucket.data.id
|
|
|
|
rule {
|
|
apply_server_side_encryption_by_default {
|
|
sse_algorithm = "aws:kms"
|
|
kms_master_key_id = aws_kms_key.s3.arn
|
|
}
|
|
}
|
|
}
|
|
|
|
resource "aws_s3_bucket_public_access_block" "data" {
|
|
bucket = aws_s3_bucket.data.id
|
|
|
|
block_public_acls = true
|
|
block_public_policy = true
|
|
ignore_public_acls = true
|
|
restrict_public_buckets = true
|
|
}
|
|
```
|
|
|
|
## Ephemeral resources
|
|
|
|
Ephemeral resources prevent sensitive data being stored in state.
|
|
For more information on ephemeral resources, see the
|
|
[Terraform documentation](https://developer.hashicorp.com/terraform/language/block/ephemeral).
|
|
|
|
Before you generate code for an ephemeral resource, check that the Terraform
|
|
version is greater than or equal to 1.11.0.
|
|
|
|
Then, follow this priority order for managing sensitive attributes:
|
|
|
|
1. **First priority: Native secrets manager integration**
|
|
If a resource has the ability to automatically manage a sensitive attribute by
|
|
storing it in a secrets manager (e.g., AWS Secrets Manager, Azure Key Vault),
|
|
use that configuration. This is the preferred approach.
|
|
|
|
```hcl
|
|
# Bad
|
|
resource "aws_rds_cluster" "example" {
|
|
cluster_identifier = "example"
|
|
database_name = "test"
|
|
master_username = "test"
|
|
master_password = var.db_master_password
|
|
}
|
|
|
|
# Good, managed by AWS Secrets Manager by default
|
|
resource "aws_rds_cluster" "test" {
|
|
cluster_identifier = "example"
|
|
database_name = "test"
|
|
manage_master_user_password = true
|
|
master_username = "test"
|
|
}
|
|
```
|
|
|
|
2. **Second priority: Write-only attributes with ephemeral resources**
|
|
If a resource has a write-only attribute but no native secrets manager integration,
|
|
use an `ephemeral` resource for the sensitive data and pass that to the write-only
|
|
attribute. Default the write-only version to 1.
|
|
|
|
```hcl
|
|
# Bad
|
|
resource "random_password" "password" {
|
|
length = 16
|
|
special = true
|
|
override_special = "!#$%&*()-_=+[]{}<>:?"
|
|
}
|
|
|
|
resource "vault_kv_secret_v2" "example" {
|
|
mount = vault_mount.kvv2.path
|
|
name = "secret"
|
|
|
|
data_json = jsonencode(
|
|
{
|
|
password = "${random_password.password.result}",
|
|
}
|
|
)
|
|
}
|
|
|
|
# Good
|
|
ephemeral "random_password" "password" {
|
|
length = 16
|
|
special = true
|
|
override_special = "!#$%&*()-_=+[]{}<>:?"
|
|
}
|
|
|
|
resource "vault_kv_secret_v2" "example" {
|
|
mount = vault_mount.kvv2.path
|
|
name = "secret"
|
|
|
|
data_json_wo = jsonencode(
|
|
{
|
|
password = "${ephemeral.random_password.password.result}",
|
|
}
|
|
)
|
|
data_json_wo_version = 1
|
|
}
|
|
```
|
|
|
|
If you need to retrieve a secret from a secrets manager to pass
|
|
to a resource, use the `ephemeral` version of the resource to
|
|
retrieve the secret and pass it to another resource.
|
|
|
|
```hcl
|
|
# Good
|
|
ephemeral "vault_kv_secret_v2" "db_secret" {
|
|
mount = vault_mount.kvv2.path
|
|
mount_id = vault_mount.kvv2.id
|
|
name = vault_kv_secret_v2.db_root.name
|
|
}
|
|
|
|
resource "vault_database_secret_backend_connection" "postgres" {
|
|
backend = vault_mount.db.path
|
|
name = "postrgres-db"
|
|
allowed_roles = ["*"]
|
|
|
|
postgresql {
|
|
connection_url = "postgresql://{{username}}:{{password}}@localhost:5432/postgres"
|
|
password_authentication = ""
|
|
username = "postgres"
|
|
password_wo = tostring(ephemeral.vault_kv_secret_v2.db_secret.data.password)
|
|
password_wo_version = 1
|
|
}
|
|
}
|
|
```
|
|
|
|
3. **Last resort: Regular resources**
|
|
Only use a regular resource that has sensitive data written to state if neither of the above
|
|
options are available, resource does not offer a write-only attribute or ephemeral resource
|
|
alternative, or the Terraform version is less than 1.11.0.
|