diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b0e4845 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.terraform +.terraform.lock.hcl \ No newline at end of file diff --git a/ReadMe.md b/ReadMe.md new file mode 100644 index 0000000..c704b5f --- /dev/null +++ b/ReadMe.md @@ -0,0 +1,62 @@ +# Terraform Providers Repository + +This repository contains configurations and documentation for various Terraform providers. Each provider listed below is used to manage specific resources and services. The repository is structured to help users integrate these providers into their Terraform projects. + +## Providers + +Below is a list of the Terraform providers referenced in this repository: + +1. [Authentik Provider](https://registry.terraform.io/providers/goauthentik/authentik/latest/docs) + Manage resources for the Authentik identity provider. + +2. [NGINX Proxy Manager Provider](https://registry.terraform.io/providers/Sander0542/nginxproxymanager/latest) + Manage resources for NGINX Proxy Manager. + +3. [Portainer Provider](https://registry.terraform.io/providers/portainer/portainer/latest) + Manage resources for Portainer, a container management platform. + +4. [Technitium Provider](https://registry.terraform.io/providers/kevynb/technitium/latest) + Manage resources for Technitium DNS Server. + +5. [Cloudflare Provider](https://registry.terraform.io/providers/cloudflare/cloudflare/5.4.0/docs/resources/dns_record) + Manage DNS records and other resources for Cloudflare. + +## Usage + +To use any of the providers listed above, include the corresponding provider block in your Terraform configuration file. Below is an example of how to configure a provider: + +```hcl +provider "cloudflare" { + email = "your-email@example.com" + api_key = "your-api-key" +} +``` + +Refer to the official documentation linked above for detailed usage instructions for each provider. + +### Prerequisites +Before using this repository, ensure you have the following installed: + +- Terraform (latest version recommended) +- Access credentials for the services you intend to manage (e.g., API keys, tokens, etc.) + +### Getting Started +1. Clone this repository: + +```shell +git clone +cd +``` + +2. Initialize Terraform: + +```shell +terraform init +``` + +3. Customize the configuration files to suit your environment. +4. Apply the configuration: + +```shell +terraform apply +``` \ No newline at end of file diff --git a/modules/authentik/directory.tf b/modules/authentik/directory.tf new file mode 100644 index 0000000..ddb4ce8 --- /dev/null +++ b/modules/authentik/directory.tf @@ -0,0 +1,18 @@ +resource "authentik_user" "dccoder" { + username = "DCCoder" + email = var.dccoder_email +} + +resource "authentik_user" "name" { + for_each = { for user in var.users : user.username => user } + + username = each.value.username + email = each.value.email + password = each.value.password +} + +resource "authentik_group" "group" { + name = "tf_admins" + users = [authentik_user.dccoder.id] + is_superuser = true +} \ No newline at end of file diff --git a/main.tf b/modules/authentik/main.tf similarity index 100% rename from main.tf rename to modules/authentik/main.tf diff --git a/modules/authentik/providers.tf b/modules/authentik/providers.tf new file mode 100644 index 0000000..27f661b --- /dev/null +++ b/modules/authentik/providers.tf @@ -0,0 +1,13 @@ +terraform { + required_providers { + authentik = { + source = "goauthentik/authentik" + version = "2025.4.0" + } + } +} + +provider "authentik" { +} + +# SEE: https://registry.terraform.io/providers/goauthentik/authentik/latest/docs \ No newline at end of file diff --git a/modules/authentik/variables.tf b/modules/authentik/variables.tf new file mode 100644 index 0000000..ab2e686 --- /dev/null +++ b/modules/authentik/variables.tf @@ -0,0 +1,15 @@ +variable "dccoder_email" { + description = "My email address" + default = "" +} + +variable "users" { + description = "List of users to create in authentik" + type = list(object({ + username = string + email = string + password = string + })) + default = [] + +} \ No newline at end of file diff --git a/modules/dns/cloudflare.tf b/modules/dns/cloudflare.tf new file mode 100644 index 0000000..5974e00 --- /dev/null +++ b/modules/dns/cloudflare.tf @@ -0,0 +1,21 @@ +resource "cloudflare_dns_record" "ipv4_dns_record" { + count = var.internal_only ? 0 : var.enable_ipv4 ? 1 : 0 + zone_id = var.zone_id + comment = "Managed via terraform" + content = var.external_host_ipv4 + name = var.domain_name + proxied = var.proxied_domain + ttl = var.ttl + type = "A" +} + +resource "cloudflare_dns_record" "ipv6_dns_record" { + count = var.internal_only ? 0 : var.enable_ipv6 ? 1 : 0 + zone_id = var.zone_id + comment = "Managed via terraform" + content = var.external_host_ipv6 + name = var.domain_name + proxied = var.proxied_domain + ttl = var.ttl + type = "AAAA" +} \ No newline at end of file diff --git a/modules/dns/nginxproxy.tf b/modules/dns/nginxproxy.tf new file mode 100644 index 0000000..d995342 --- /dev/null +++ b/modules/dns/nginxproxy.tf @@ -0,0 +1,47 @@ +module "nginx" { + source = "../nginx_config" +} + +resource "nginxproxymanager_certificate_letsencrypt" "certificate" { + domain_names = [var.domain_name] + + letsencrypt_email = var.admin_email + letsencrypt_agree = true + + dns_challenge = true + dns_provider = "cloudflare" + dns_provider_credentials = var.dns_cloudflare_api_token +} + +data "nginxproxymanager_access_list" "access_list" { + id = var.internal_only ? module.nginx.outputs.internal_access_list_id : module.nginx.outputs.cloudflare_access_list_id +} + +resource "nginxproxymanager_proxy_host" "host" { + domain_names = [var.domain_name] + + forward_scheme = "https" + forward_host = var.domain_name + forward_port = 443 + + caching_enabled = true + allow_websocket_upgrade = true + block_exploits = true + + access_list_id = data.nginxproxymanager_access_list.access_list.id + + locations = [ + { + path = "/" + forward_scheme = "http" + forward_host = var.internal_host_ipv4 != "" ? var.internal_host_ipv4 : var.internal_host_ipv6 + forward_port = var.service_port + } + ] + + certificate_id = nginxproxymanager_certificate_letsencrypt.certificate.id + ssl_forced = true + hsts_enabled = false + hsts_subdomains = false + http2_support = true +} \ No newline at end of file diff --git a/modules/dns/providers.tf b/modules/dns/providers.tf new file mode 100644 index 0000000..4d67836 --- /dev/null +++ b/modules/dns/providers.tf @@ -0,0 +1,18 @@ +terraform { + required_providers { + nginxproxymanager = { + source = "Sander0542/nginxproxymanager" + version = "1.1.1" + } + + cloudflare = { + source = "cloudflare/cloudflare" + version = "~> 5" + } + + technitium = { + source = "kevynb/technitium" + version = "0.2.0" + } + } +} \ No newline at end of file diff --git a/modules/dns/technitium.tf b/modules/dns/technitium.tf new file mode 100644 index 0000000..1be06df --- /dev/null +++ b/modules/dns/technitium.tf @@ -0,0 +1,17 @@ +resource "technitium_record" "ipv4_dns_record" { + count = var.enable_ipv4 ? 1 : 0 + #zone = var.zone_name + domain = var.domain_name + type = "A" + ip_address = var.internal_host_ipv4 + ttl = var.ttl +} + +resource "technitium_record" "ipv6_dns_record" { + count = var.enable_ipv6 ? 1 : 0 + #zone = var.zone_name + domain = var.domain_name + type = "AAAA" + ip_address = var.internal_host_ipv6 + ttl = var.ttl +} \ No newline at end of file diff --git a/modules/dns/variables.tf b/modules/dns/variables.tf new file mode 100644 index 0000000..1bc603f --- /dev/null +++ b/modules/dns/variables.tf @@ -0,0 +1,87 @@ +variable "internal_only" { + description = "If true, the application is only accessible internally" + type = bool + default = false +} + +variable "service_port" { + description = "Port on which the service is running" + type = number + default = 80 +} + +variable "dns_cloudflare_api_token" { + description = "Cloudflare API token for DNS updates" + type = string + default = "" + sensitive = true +} + +variable "admin_email" { + description = "Email address for the admin user" + type = string + default = "" +} + +variable "external_host_ipv4" { + description = "External host for the application" + type = string + default = "" +} + +variable "external_host_ipv6" { + description = "External host for the application" + type = string + default = "" +} + +variable "internal_host_ipv4" { + description = "Internal host for the application" + type = string + default = "" +} + +variable "internal_host_ipv6" { + description = "Internal host for the application" + type = string + default = "" +} + +variable "enable_ipv4" { + description = "If true, enable IPv4 for the application" + type = bool + default = true +} + +variable "enable_ipv6" { + description = "If true, enable IPv6 for the application" + type = bool + default = false +} + +variable "zone_name" { + description = "Zone name for the DNS record" + type = string +} + +variable "domain_name" { + description = "Domain name for the application" + type = string +} + +variable "zone_id" { + description = "Cloudflare zone ID for the DNS record" + type = string +} + +variable "proxied_domain" { + description = "If true, the DNS record is proxied through Cloudflare" + type = bool + default = true +} + +variable "ttl" { + description = "Time to live for the DNS record" + type = number + default = 3600 +} \ No newline at end of file diff --git a/modules/nginx_config/main.tf b/modules/nginx_config/main.tf new file mode 100644 index 0000000..c2c4abd --- /dev/null +++ b/modules/nginx_config/main.tf @@ -0,0 +1,127 @@ +resource "nginxproxymanager_access_list" "internal_access" { + name = "Allow Internal Access Only" + + access = [ + { + directive = "allow" + address = "192.168.1.1/24" + }, + { + directive = "allow" + address = "192.168.2.1/24" + }, + { + directive = "allow" + address = "192.168.3.1/23" + }, + { + directive = "deny" + address = "all" + }, + ] + + pass_auth = false + satisfy_any = true +} + +resource "nginxproxymanager_access_list" "cloudflare_access" { + name = "Allow Cloudflare Access Only" + + access = [ + { + directive = "allow" + address = "103.21.244.0/22" + }, + { + directive = "allow" + address = "103.22.200.0/22" + }, + { + directive = "allow" + address = "103.31.4.0/22" + }, + { + directive = "allow" + address = "104.16.0.0/13" + }, + { + directive = "allow" + address = "104.24.0.0/14" + }, + { + directive = "allow" + address = "108.162.192.0/18" + }, + { + directive = "allow" + address = "131.0.72.0/22" + }, + { + directive = "allow" + address = "141.101.64.0/18" + }, + { + directive = "allow" + address = "162.158.0.0/15" + }, + { + directive = "allow" + address = "172.64.0.0/13" + }, + { + directive = "allow" + address = "173.245.48.0/20" + }, + { + directive = "allow" + address = "188.114.96.0/20" + }, + { + directive = "allow" + address = "190.93.240.0/20" + }, + { + directive = "allow" + address = "197.234.240.0/22" + }, + { + directive = "allow" + address = "198.41.128.0/17" + }, + { + directive = "allow" + address = "2400:cb00::/32" + }, + { + directive = "allow" + address = "2606:4700::/32" + }, + { + directive = "allow" + address = "2803:f800::/32" + }, + { + directive = "allow" + address = "2405:b500::/32" + }, + { + directive = "allow" + address = "2405:8100::/32" + }, + { + directive = "allow" + address = "2a06:98c0::/29" + }, + { + directive = "allow" + address = "2c0f:f248::/32" + }, + { + directive = "deny" + address = "all" + }, + ] + + pass_auth = false + satisfy_any = true +} \ No newline at end of file diff --git a/modules/nginx_config/outputs.tf b/modules/nginx_config/outputs.tf new file mode 100644 index 0000000..61539a1 --- /dev/null +++ b/modules/nginx_config/outputs.tf @@ -0,0 +1,11 @@ +output "internal_access_list_id" { + description = "ID of the internal access list" + value = nginxproxymanager_access_list.internal_access.id + +} + +output "cloudflare_access_list_id" { + description = "ID of the Cloudflare access list" + value = nginxproxymanager_access_list.cloudflare_access.id + +} \ No newline at end of file diff --git a/modules/nginx_config/providers.tf b/modules/nginx_config/providers.tf new file mode 100644 index 0000000..7bb4c29 --- /dev/null +++ b/modules/nginx_config/providers.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + nginxproxymanager = { + source = "Sander0542/nginxproxymanager" + version = "1.1.1" + } + } +} \ No newline at end of file diff --git a/modules/oauth_auth/main.tf b/modules/oauth_auth/main.tf new file mode 100644 index 0000000..1421074 --- /dev/null +++ b/modules/oauth_auth/main.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + authentik = { + source = "goauthentik/authentik" + version = "2025.4.0" + } + } +} \ No newline at end of file diff --git a/modules/oauth_auth/outputs.tf b/modules/oauth_auth/outputs.tf new file mode 100644 index 0000000..1e24c03 --- /dev/null +++ b/modules/oauth_auth/outputs.tf @@ -0,0 +1,9 @@ +output "client_id" { + description = "Shared client ID for the OAuth2 provider" + value = authentik_provider_oauth2.name.client_id +} + +output "client_secret" { + description = "Shared client secret for the OAuth2 provider" + value = authentik_provider_oauth2.name.client_secret +} \ No newline at end of file diff --git a/modules/oauth_auth/policy.tf b/modules/oauth_auth/policy.tf new file mode 100644 index 0000000..0dd838a --- /dev/null +++ b/modules/oauth_auth/policy.tf @@ -0,0 +1,18 @@ +data "authentik_group" "access_group" { + count = var.access_group == null ? 0 : 1 + name = var.access_group_name +} + +# Create a policy for the specified group +resource "authentik_policy_group" "access_policy" { + count = var.access_group == null ? 0 : 1 + name = "${var.name}-group-policy" + group = data.authentik_group.access_group[0].id +} + +resource "authentik_policy_binding" "app_binding" { + count = var.access_group == null ? 0 : 1 + target = authentik_application.name.uuid + policy = authentik_policy_group.access_policy[0].id + order = 0 +} \ No newline at end of file diff --git a/modules/oauth_auth/service.tf b/modules/oauth_auth/service.tf new file mode 100644 index 0000000..8afef9f --- /dev/null +++ b/modules/oauth_auth/service.tf @@ -0,0 +1,34 @@ +resource "random_string" "example" { + length = 16 + special = false + upper = true + lower = true + numeric = true + override_special = "!@#$%^&*" +} + +data "authentik_flow" "default-authorization-flow" { + slug = "default-provider-authorization-implicit-consent" +} + +data "authentik_flow" "default-invalidation-flow" { + slug = "default-invalidation-flow" +} + +# Create an OAuth2 Provider + +resource "authentik_provider_oauth2" "name" { + name = lower(replace(var.name, " ", "-")) + client_id = random_string.example.result + authorization_flow = authentik_flow.default-authorization-flow.id + invalidation_flow = authentik_flow.default-invalidation-flow.id + allowed_redirect_uris = var.redirect_uris +} + +resource "authentik_application" "name" { + name = var.name + meta_description = var.description + group = var.group + slug = lower(replace(var.name, " ", "-")) + protocol_provider = authentik_provider_oauth2.name.id +} \ No newline at end of file diff --git a/modules/oauth_auth/variables.tf b/modules/oauth_auth/variables.tf new file mode 100644 index 0000000..3cad301 --- /dev/null +++ b/modules/oauth_auth/variables.tf @@ -0,0 +1,53 @@ +variable "internal_host" { + description = "If true, the application is only accessible internally" + type = bool +} + +variable "external_host" { + description = "If true, the application is only accessible internally" + type = bool +} + +variable "name" { + description = "Name of the application" + type = string +} + +variable "description" { + description = "Description of the application" + type = string + default = "" +} + +variable "group" { + description = "Group to assign the application to" + type = string + default = "" +} + +variable "access_group" { + description = "Group to assign access to the application" + type = string + default = "" +} + +variable "username_attribute" { + description = "The attribute to use for the username in basic auth" + type = string + default = "username" +} + +variable "password_attribute" { + description = "The attribute to use for the password in basic auth" + type = string + default = "password" +} + +variable "redirect_uris" { + description = "List of redirect URIs for the OAuth2 provider" + type = list(string) + default = [ + "https://example.com/callback", + "http://localhost:3000/callback" + ] +} \ No newline at end of file diff --git a/modules/proxy_auth/main.tf b/modules/proxy_auth/main.tf new file mode 100644 index 0000000..1421074 --- /dev/null +++ b/modules/proxy_auth/main.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + authentik = { + source = "goauthentik/authentik" + version = "2025.4.0" + } + } +} \ No newline at end of file diff --git a/modules/proxy_auth/policy.tf b/modules/proxy_auth/policy.tf new file mode 100644 index 0000000..0dd838a --- /dev/null +++ b/modules/proxy_auth/policy.tf @@ -0,0 +1,18 @@ +data "authentik_group" "access_group" { + count = var.access_group == null ? 0 : 1 + name = var.access_group_name +} + +# Create a policy for the specified group +resource "authentik_policy_group" "access_policy" { + count = var.access_group == null ? 0 : 1 + name = "${var.name}-group-policy" + group = data.authentik_group.access_group[0].id +} + +resource "authentik_policy_binding" "app_binding" { + count = var.access_group == null ? 0 : 1 + target = authentik_application.name.uuid + policy = authentik_policy_group.access_policy[0].id + order = 0 +} \ No newline at end of file diff --git a/modules/proxy_auth/service.tf b/modules/proxy_auth/service.tf new file mode 100644 index 0000000..4b60f50 --- /dev/null +++ b/modules/proxy_auth/service.tf @@ -0,0 +1,23 @@ +# Create a proxy provider + +data "authentik_flow" "default-authorization-flow" { + slug = "default-provider-authorization-implicit-consent" +} + +resource "authentik_provider_proxy" "name" { + name = lower(replace(var.name, " ", "-")) + internal_host = var.internal_host + external_host = var.external_host + basic_auth_password_attribute = var.password_attribute + basic_auth_username_attribute = var.username_attribute + basic_auth_enabled = true + authorization_flow = data.authentik_flow.default-authorization-flow.id +} + +resource "authentik_application" "name" { + name = var.name + meta_description = var.description + group = var.group + slug = lower(replace(var.name, " ", "-")) + protocol_provider = authentik_provider_proxy.name.id +} \ No newline at end of file diff --git a/modules/proxy_auth/variables.tf b/modules/proxy_auth/variables.tf new file mode 100644 index 0000000..a6d6395 --- /dev/null +++ b/modules/proxy_auth/variables.tf @@ -0,0 +1,44 @@ +variable "internal_host" { + description = "If true, the application is only accessible internally" + type = bool +} + +variable "external_host" { + description = "If true, the application is only accessible internally" + type = bool +} + +variable "name" { + description = "Name of the application" + type = string +} + +variable "description" { + description = "Description of the application" + type = string + default = "" +} + +variable "group" { + description = "Group to assign the application to" + type = string + default = "" +} + +variable "access_group" { + description = "Group to assign access to the application" + type = string + default = "" +} + +variable "username_attribute" { + description = "The attribute to use for the username in basic auth" + type = string + default = "username" +} + +variable "password_attribute" { + description = "The attribute to use for the password in basic auth" + type = string + default = "password" +} \ No newline at end of file diff --git a/variables.tf b/variables.tf deleted file mode 100644 index e69de29..0000000