diff --git a/03_keyvault.tf b/03_keyvault.tf index c7e7ca1..df84b60 100644 --- a/03_keyvault.tf +++ b/03_keyvault.tf @@ -17,7 +17,6 @@ resource "azurerm_key_vault" "az_openai_kv" { depends_on = [azurerm_subnet.az_openai_subnet] } - # Add "self" permission to key vault RBAC (to manange key vault secrets) resource "azurerm_role_assignment" "kv_role_assigment" { for_each = toset(["Key Vault Administrator"]) diff --git a/06_librechat_app.tf b/06_librechat_app.tf index a2848b2..fb3d40b 100644 --- a/06_librechat_app.tf +++ b/06_librechat_app.tf @@ -164,7 +164,7 @@ resource "azurerm_app_service_custom_hostname_binding" "hostname_binding" { app_service_name = var.libre_app_name resource_group_name = azurerm_resource_group.az_openai_rg.name - depends_on = [azurerm_dns_cname_record.cname_record, azurerm_linux_web_app.librechat ] + depends_on = [azurerm_dns_cname_record.cname_record, azurerm_linux_web_app.librechat] } resource "azurerm_app_service_managed_certificate" "libre_app_cert" { diff --git a/06_librechat_app_config.tf b/06_librechat_app_config.tf index 54227a2..f5bb56a 100644 --- a/06_librechat_app_config.tf +++ b/06_librechat_app_config.tf @@ -60,6 +60,33 @@ locals { REFRESH_TOKEN_EXPIRY = (1000 * 60 * 60 * 24) * 5 #7 days JWT_SECRET = var.libre_app_jwt_secret != null ? var.libre_app_jwt_secret : "@Microsoft.KeyVault(SecretUri=${azurerm_key_vault_secret.libre_app_jwt_secret.id})" JWT_REFRESH_SECRET = var.libre_app_jwt_refresh_secret != null ? var.libre_app_jwt_refresh_secret : "@Microsoft.KeyVault(SecretUri=${azurerm_key_vault_secret.libre_app_jwt_refresh_secret.id})" + + ### User Violations ### + BAN_VIOLATIONS = var.libre_app_violations.enabled + BAN_DURATION = var.libre_app_violations.ban_duration + BAN_INTERVAL = var.libre_app_violations.ban_interval + + LOGIN_VIOLATION_SCORE = var.libre_app_violations.login_violation_score + REGISTRATION_VIOLATION_SCORE = var.libre_app_violations.registration_violation_score + CONCURRENT_VIOLATION_SCORE = var.libre_app_violations.concurrent_violation_score + MESSAGE_VIOLATION_SCORE = var.libre_app_violations.message_violation_score + NON_BROWSER_VIOLATION_SCORE = var.libre_app_violations.non_browser_violation_score + + LOGIN_MAX = var.libre_app_violations.login_max + LOGIN_WINDOW = var.libre_app_violations.login_window + REGISTER_MAX = var.libre_app_violations.register_max + REGISTER_WINDOW = var.libre_app_violations.register_window + + LIMIT_CONCURRENT_MESSAGES = var.libre_app_violations.limit_concurrent_messages + CONCURRENT_MESSAGE_MAX = var.libre_app_violations.concurrent_message_max + + LIMIT_MESSAGE_IP = var.libre_app_violations.limit_message_ip + MESSAGE_IP_MAX = var.libre_app_violations.message_ip_max + MESSAGE_IP_WINDOW = var.libre_app_violations.message_ip_window + + LIMIT_MESSAGE_USER = var.libre_app_violations.limit_message_user + MESSAGE_USER_MAX = var.libre_app_violations.message_user_max + MESSAGE_USER_WINDOW = var.libre_app_violations.message_user_window } } diff --git a/README.md b/README.md index a05cdfa..f87fbf6 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,22 @@ New integrations and features have been added to the module to use the latest ** ## Coming next - Feature development -- [x] Custom domain support with managed certificates (Released in v2.1.0) +### v2.3.x + - [ ] Privatise solution with Private endpoint support - [ ] Front Door and WAF support for public entrypoint - [ ] Azure AI Search/MeiliSearch Integration - [ ] Add additional support for [Azure OpenAI DALL-E-3](https://docs.microsoft.com/en-us/azure/cognitive-services/openai-dall-e/overview) -- [ ] Updated documentation and examples + +### v2.2.x + +- [x] Added User Violations Support +- [x] Updated documentation and examples +- [x] Added module outputs + +### v2.1.x + +- [x] Custom domain support with managed certificates ## Legacy Version 1.x @@ -51,9 +61,23 @@ coming soon... ## Examples -coming soon... +- [Public Deployment with Custom Domain and IP Whitelisting](https://github.com/Pwd9000-ML/terraform-azurerm-openai-private-chatgpt/tree/master/examples/public_deployment_with_custom_domain) + +## Contributing + +Contributions are welcome. Please submit a pull request if you have any improvements or fixes. Make sure to follow the existing code style and add comments to your code explaining what it does. + +## License + +This terraform module is licensed under the MIT License. See the LICENSE file for more details. + +## Support + +If you encounter any issues or have any questions about this terraform module, please open an issue on GitHub. We'll do our best to respond as quickly as possible. + +## Acknowledgements -Enjoy! +This terraform module was developed by **Marcel Lupo** as part of a project to explore the capabilities of Azure OpenAI models. We'd like to thank the OpenAI and Microsoft team for their incredible work and ongoing support of the AI community. ## Requirements diff --git a/examples/Coming_soon.md b/examples/private_deployment_with_front_door/Coming_soon.md similarity index 100% rename from examples/Coming_soon.md rename to examples/private_deployment_with_front_door/Coming_soon.md diff --git a/examples/public_deployment_with_custom_domain/README.md b/examples/public_deployment_with_custom_domain/README.md new file mode 100644 index 0000000..64e5600 --- /dev/null +++ b/examples/public_deployment_with_custom_domain/README.md @@ -0,0 +1,58 @@ +# Public Deployment with Custom Domain and IP Whitelisting + +This example contains a Terraform script for provisioning a set of Azure resources for a chat application powered by Azure OpenAI models. The script is designed to be modular and configurable, allowing you to customise the resources and settings to fit your specific needs. + +In this example the Chat App is deployed with a public endpoint, a custom domain name and managed certificate. The access to the app can be restricted to a set of whitelisted IP addresses or subnets using these variables: + +```hcl +libre_app_virtual_network_subnet_id = null # Access is allowed on the built in subnet of this module. If networking is created as part of the module, this will be automatically populated if value is 'null' (priority 100) +libre_app_allowed_subnets = null # Add any other subnet ids to allow access to the app service (optional) +libre_app_allowed_ip_addresses = [ + { + ip_address = "0.0.0.0/0" # (Change to your IP address or CIDR range) + priority = 200 + name = "ip-access-rule1" + action = "Allow" + } +] +``` + +## Resources + +The script provisions the following resources: + +- **Random Integer**: This is used to generate unique names for the resources to avoid naming conflicts. +- **Resource Group**: This is the resource group that contains all the resources for the chat application. +- **Virtual Network**: This is the virtual network and subnet that contains the chat application and supporting resources. +- **Key Vault**: This is used to store secrets for the chat application, such as the OpenAI API key and other secrets used by the application. +- **OpenAI Service**: This is the core service that powers the chat application. It uses OpenAI's GPT-3 model to generate responses to user inputs. +- **CosmosDB Instance**: This is the database for the chat application. It stores user data and chat logs. +- **App Service**: This hosts the chat application. It's configured with various settings for networking, storage, and access control. + +## Configuration + +The script uses variables for configuration. These variables can be set in a `common.auto.tfvars` file or passed in via the command line when running `terraform apply`. + +The variables include settings for the location, tags, resource group name, virtual network name, subnet configuration, key vault settings, OpenAI service settings, CosmosDB settings, and App Service settings. + +## Usage + +To use this script, you need to have Terraform installed. You can then clone this repository and run `terraform init` to initialize your Terraform workspace. Once the workspace is initialized, you can run `terraform apply` to create the resources. + +Please note that you will need to provide values for all the required variables in the `common.auto.tfvars` file and defining the variables there, or by passing them in via the command line when running `terraform apply`. + +## Contributing + +Contributions are welcome. Please submit a pull request if you have any improvements or fixes. Make sure to follow the existing code style and add comments to your code explaining what it does. + +## License + +This script is licensed under the MIT License. See the LICENSE file for more details. + +## Support + +If you encounter any issues or have any questions about this script, please open an issue on GitHub. We'll do our best to respond as quickly as possible. + +## Acknowledgements + +This script was developed by **Marcel Lupo** as part of a project to explore the capabilities of Azure OpenAI models. We'd like to thank the OpenAI and Microsoft team for their incredible work and ongoing support of the AI community. diff --git a/examples/public_deployment_with_custom_domain/common.auto.tfvars b/examples/public_deployment_with_custom_domain/common.auto.tfvars new file mode 100644 index 0000000..726caf1 --- /dev/null +++ b/examples/public_deployment_with_custom_domain/common.auto.tfvars @@ -0,0 +1,198 @@ +### 01 Common Variables + RG ### +resource_group_name = "Private-ChatGPT-OpenAI-LibreChat-Example" +location = "SwedenCentral" +tags = { + Terraform = "True" + Description = "Private ChatGPT hosted on Azure OpenAI (Librechat)" + Author = "Marcel Lupo" + GitHub = "https://github.com/Pwd9000-ML/terraform-azurerm-openai-private-chatgpt" +} + +### 02 networking ### +virtual_network_name = "demogptvnet" +vnet_address_space = ["10.3.0.0/24"] +subnet_config = { + subnet_name = "demogpt-sub" + subnet_address_space = ["10.3.0.0/24"] + service_endpoints = ["Microsoft.AzureCosmosDB", "Microsoft.Web", "Microsoft.KeyVault"] + private_endpoint_network_policies_enabled = false + private_link_service_network_policies_enabled = false + subnets_delegation_settings = { + app-service-plan = [ + { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + ] + } +} + +### 03 KeyVault ### +kv_name = "demogptkv" +kv_sku = "standard" +kv_fw_default_action = "Deny" +kv_fw_bypass = "AzureServices" +kv_fw_allowed_ips = ["0.0.0.0/0"] # Allow all IPs (for demo purposes) +kv_fw_network_subnet_ids = null # leave null to allow access from default subnet of this module + +### 04 Create OpenAI Service ### +oai_account_name = "demogptoai" +oai_sku_name = "S0" +oai_custom_subdomain_name = "demogptoai" +oai_dynamic_throttling_enabled = false +oai_fqdns = [] +oai_local_auth_enabled = true +oai_outbound_network_access_restricted = false +oai_public_network_access_enabled = true +oai_customer_managed_key = null +oai_identity = { + type = "SystemAssigned" +} +oai_network_acls = null +oai_storage = null +oai_model_deployment = [ + { + deployment_id = "gpt-35-turbo" + model_name = "gpt-35-turbo" + model_format = "OpenAI" + model_version = "1106" + scale_type = "Standard" + scale_capacity = 20 # 34K == Roughly 204 RPM (Requests per minute) + }, + { + deployment_id = "gpt-4" + model_name = "gpt-4" + model_format = "OpenAI" + model_version = "1106-Preview" + scale_type = "Standard" + scale_capacity = 20 + }, + { + deployment_id = "gpt-4-vision-preview" + model_name = "gpt-4" + model_format = "OpenAI" + model_version = "vision-preview" + scale_type = "Standard" + scale_capacity = 5 + }, + { + deployment_id = "dall-e-3" + model_name = "dall-e-3" + model_format = "OpenAI" + model_version = "3.0" + scale_type = "Standard" + scale_capacity = 2 + } +] + +### 05 cosmosdb ### +cosmosdb_name = "demogptcosmosdb" +cosmosdb_offer_type = "Standard" +cosmosdb_kind = "MongoDB" +cosmosdb_automatic_failover = false +use_cosmosdb_free_tier = true +cosmosdb_consistency_level = "BoundedStaleness" +cosmosdb_max_interval_in_seconds = 10 +cosmosdb_max_staleness_prefix = 200 +cosmosdb_geo_locations = [ + { + location = "SwedenCentral" + failover_priority = 0 + } +] +cosmosdb_capabilities = ["EnableMongo", "MongoDBv3.4"] +cosmosdb_virtual_network_subnets = null # leave null to allow access from default subnet of this module +cosmosdb_is_virtual_network_filter_enabled = true +cosmosdb_public_network_access_enabled = true + +### 06 app services (librechat app + meilisearch) ### +# App Service Plan +app_service_name = "demogptasp" +app_service_sku_name = "B1" + +# LibreChat App Service +libre_app_name = "demogptchatapp" +libre_app_public_network_access_enabled = true +libre_app_virtual_network_subnet_id = null # Access is allowed on the built in subnet of this module. If networking is created as part of the module, this will be automatically populated if value is 'null' (priority 100) +libre_app_allowed_subnets = null # Add any other subnet ids to allow access to the app service (optional) +libre_app_allowed_ip_addresses = [ + { + ip_address = "0.0.0.0/0" # Allow all IPs (for demo purposes) + priority = 200 + name = "ip-access-rule1" + action = "Allow" + } +] + +### LibreChat App Settings ### +# Server Config +libre_app_title = "PRIVATE DEMO CHATBOT" +libre_app_custom_footer = "Privately hosted GPT App powered by Azure OpenAI and LibreChat" +libre_app_host = "0.0.0.0" +libre_app_port = 80 +libre_app_docker_image = "ghcr.io/danny-avila/librechat-dev-api:81ff598eba338e680c91e237cea3e3df870bce23" #v0.6.6 (Pre-release) +libre_app_mongo_uri = null # leave null to use the cosmosdb uri saved in keyvault created by this module +libre_app_domain_client = "http://localhost:80" +libre_app_domain_server = "http://localhost:80" + +# debug logging +libre_app_debug_logging = true +libre_app_debug_console = false + +# Endpoints +libre_app_endpoints = "azureOpenAI" + +# Azure OpenAI +libre_app_az_oai_api_key = null # leave null to use the key saved in keyvault created by this module +libre_app_az_oai_models = "gpt-35-turbo,gpt-4,gpt-4-vision-preview" +libre_app_az_oai_use_model_as_deployment_name = true +libre_app_az_oai_instance_name = null # leave null to use the instance name created by this module +libre_app_az_oai_api_version = "2023-07-01-preview" +libre_app_az_oai_dall3_api_version = "2023-12-01-preview" +libre_app_az_oai_dall3_deployment_name = "dall-e-3" + +# Plugins +libre_app_debug_plugins = true +libre_app_plugins_creds_key = null # leave null to use the key saved in keyvault created by this module +libre_app_plugins_creds_iv = null # leave null to use the iv saved in keyvault created by this module + +# Search +libre_app_enable_meilisearch = false + +# User Registration +libre_app_allow_email_login = true +libre_app_allow_registration = true +libre_app_allow_social_login = false +libre_app_allow_social_registration = false +libre_app_jwt_secret = null # leave null to use the secret saved in keyvault created by this module +libre_app_jwt_refresh_secret = null # leave null to use the refresh secret saved in keyvault created by this module + +# violations +libre_app_violations = { + enabled = false + ban_duration = 1000 * 60 * 60 * 2 + ban_interval = 20 + login_violation_score = 1 + registration_violation_score = 1 + concurrent_violation_score = 1 + message_violation_score = 1 + non_browser_violation_score = 20 + login_max = 7 + login_window = 5 + register_max = 5 + register_window = 60 + limit_concurrent_messages = false + concurrent_message_max = 2 + limit_message_ip = false + message_ip_max = 40 + message_ip_window = 1 + limit_message_user = false + message_user_max = 40 + message_user_window = 1 +} + +# Custom Domain and Managed Certificate (Optional) +libre_app_custom_domain_create = true +librechat_app_custom_domain_name = "privategptchatbot" +librechat_app_custom_dns_zone_name = "domain.com" +dns_resource_group_name = "DNS-Resource-Group-Name" diff --git a/examples/public_deployment_with_custom_domain/main.tf b/examples/public_deployment_with_custom_domain/main.tf new file mode 100644 index 0000000..31d63b1 --- /dev/null +++ b/examples/public_deployment_with_custom_domain/main.tf @@ -0,0 +1,144 @@ +terraform { + #backend "azurerm" {} + backend "local" { path = "terraform-example1.tfstate" } +} + +provider "azurerm" { + features { + key_vault { + purge_soft_delete_on_destroy = true + } + } +} + +# ################################################# +# # PRE-REQS # +# ################################################# +### Random integer to generate unique names +resource "random_integer" "number" { + min = 0001 + max = 9999 +} + +module "private-chatgpt-openai" { + source = "Pwd9000-ML/openai-private-chatgpt/azurerm" + version = "~> 2.2.0" + + # 01 common + RG # + #================# + location = var.location + tags = var.tags + resource_group_name = var.resource_group_name + + # 02 networking # + #===============# + virtual_network_name = "${var.virtual_network_name}${random_integer.number.result}" + vnet_address_space = var.vnet_address_space + subnet_config = var.subnet_config + + # 03 keyvault (Solution Secrets) + #==============================# + kv_name = "${var.kv_name}${random_integer.number.result}" + kv_sku = var.kv_sku + kv_fw_default_action = var.kv_fw_default_action + kv_fw_bypass = var.kv_fw_bypass + kv_fw_allowed_ips = var.kv_fw_allowed_ips + kv_fw_network_subnet_ids = var.kv_fw_network_subnet_ids + + # 04 openai service + #==================# + oai_account_name = "${var.oai_account_name}${random_integer.number.result}" + oai_sku_name = var.oai_sku_name + oai_custom_subdomain_name = "${var.oai_custom_subdomain_name}${random_integer.number.result}" + oai_dynamic_throttling_enabled = var.oai_dynamic_throttling_enabled + oai_fqdns = var.oai_fqdns + oai_local_auth_enabled = var.oai_local_auth_enabled + oai_outbound_network_access_restricted = var.oai_outbound_network_access_restricted + oai_public_network_access_enabled = var.oai_public_network_access_enabled + oai_customer_managed_key = var.oai_customer_managed_key + oai_identity = var.oai_identity + oai_network_acls = var.oai_network_acls + oai_storage = var.oai_storage + oai_model_deployment = var.oai_model_deployment + + # 05 cosmosdb + #============# + cosmosdb_name = "${var.cosmosdb_name}${random_integer.number.result}" + cosmosdb_offer_type = var.cosmosdb_offer_type + cosmosdb_kind = var.cosmosdb_kind + cosmosdb_automatic_failover = var.cosmosdb_automatic_failover + use_cosmosdb_free_tier = var.use_cosmosdb_free_tier + cosmosdb_consistency_level = var.cosmosdb_consistency_level + cosmosdb_max_interval_in_seconds = var.cosmosdb_max_interval_in_seconds + cosmosdb_max_staleness_prefix = var.cosmosdb_max_staleness_prefix + cosmosdb_geo_locations = var.cosmosdb_geo_locations + cosmosdb_capabilities = var.cosmosdb_capabilities + cosmosdb_virtual_network_subnets = var.cosmosdb_virtual_network_subnets + cosmosdb_is_virtual_network_filter_enabled = var.cosmosdb_is_virtual_network_filter_enabled + cosmosdb_public_network_access_enabled = var.cosmosdb_public_network_access_enabled + + # 06 app services (librechat app + meilisearch) + #=============================================# + # App Service Plan + app_service_name = "${var.app_service_name}${random_integer.number.result}" + app_service_sku_name = var.app_service_sku_name + + # LibreChat App + libre_app_name = "${var.libre_app_name}${random_integer.number.result}" + libre_app_virtual_network_subnet_id = var.libre_app_virtual_network_subnet_id + libre_app_public_network_access_enabled = var.libre_app_public_network_access_enabled + libre_app_allowed_subnets = var.libre_app_allowed_subnets + libre_app_allowed_ip_addresses = var.libre_app_allowed_ip_addresses + + ### LibreChat App Settings ### + # Server Config + libre_app_title = var.libre_app_title + libre_app_custom_footer = var.libre_app_custom_footer + libre_app_host = var.libre_app_host + libre_app_port = var.libre_app_port + libre_app_docker_image = var.libre_app_docker_image + libre_app_mongo_uri = var.libre_app_mongo_uri + libre_app_domain_client = var.libre_app_domain_client + libre_app_domain_server = var.libre_app_domain_server + + # Debug Config + libre_app_debug_logging = var.libre_app_debug_logging + libre_app_debug_console = var.libre_app_debug_console + + # Endpoints + libre_app_endpoints = var.libre_app_endpoints + + # Azure OpenAI Config + libre_app_az_oai_api_key = var.libre_app_az_oai_api_key + libre_app_az_oai_models = var.libre_app_az_oai_models + libre_app_az_oai_use_model_as_deployment_name = var.libre_app_az_oai_use_model_as_deployment_name + libre_app_az_oai_instance_name = var.libre_app_az_oai_instance_name + libre_app_az_oai_api_version = var.libre_app_az_oai_api_version + libre_app_az_oai_dall3_api_version = var.libre_app_az_oai_dall3_api_version + libre_app_az_oai_dall3_deployment_name = var.libre_app_az_oai_dall3_deployment_name + + # Plugins + libre_app_debug_plugins = var.libre_app_debug_plugins + libre_app_plugins_creds_key = var.libre_app_plugins_creds_key + libre_app_plugins_creds_iv = var.libre_app_plugins_creds_iv + + # Search + libre_app_enable_meilisearch = var.libre_app_enable_meilisearch + + # User Registration + libre_app_allow_email_login = var.libre_app_allow_email_login + libre_app_allow_registration = var.libre_app_allow_registration + libre_app_allow_social_login = var.libre_app_allow_social_login + libre_app_allow_social_registration = var.libre_app_allow_social_registration + libre_app_jwt_secret = var.libre_app_jwt_secret + libre_app_jwt_refresh_secret = var.libre_app_jwt_refresh_secret + + # Violations + libre_app_violations = var.libre_app_violations + + # Custom Domain and Managed Certificate (Optional) + libre_app_custom_domain_create = var.libre_app_custom_domain_create + librechat_app_custom_domain_name = var.librechat_app_custom_domain_name + librechat_app_custom_dns_zone_name = var.librechat_app_custom_dns_zone_name + dns_resource_group_name = var.dns_resource_group_name +} \ No newline at end of file diff --git a/examples/public_deployment_with_custom_domain/variables.tf b/examples/public_deployment_with_custom_domain/variables.tf new file mode 100644 index 0000000..87b9fc2 --- /dev/null +++ b/examples/public_deployment_with_custom_domain/variables.tf @@ -0,0 +1,669 @@ +### 01 common + RG ### +variable "location" { + type = string + default = "uksouth" + description = "Azure region where resources will be hosted." +} + +variable "tags" { + type = map(string) + default = {} + description = "A map of key value pairs that is used to tag resources created." +} + +variable "resource_group_name" { + type = string + description = "Name of the resource group to create where the cognitive account OpenAI service is hosted." + nullable = false +} + +### 02 networking ### +variable "virtual_network_name" { + type = string + default = "openai-vnet-9000" + description = "Name of the virtual network where resources are attached." +} + +variable "vnet_address_space" { + type = list(string) + default = ["10.4.0.0/24"] + description = "value of the address space for the virtual network." +} + +variable "subnet_config" { + type = object({ + subnet_name = string + subnet_address_space = list(string) + service_endpoints = list(string) + private_endpoint_network_policies_enabled = bool + private_link_service_network_policies_enabled = bool + subnets_delegation_settings = map(list(object({ + name = string + actions = list(string) + }))) + }) + default = { + subnet_name = "app-cosmos-sub" + subnet_address_space = ["10.4.0.0/24"] + service_endpoints = ["Microsoft.AzureCosmosDB", "Microsoft.Web"] + private_endpoint_network_policies_enabled = false + private_link_service_network_policies_enabled = false + subnets_delegation_settings = { + app-service-plan = [ + { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + ] + } + } + description = "A list of subnet configuration objects to create subnets in the virtual network." +} + +### 03 key vault ### +variable "kv_name" { + type = string + description = "Name of the Key Vault to create (solution secrets)." + default = "openaikv9000" +} + +variable "kv_sku" { + type = string + description = "SKU of the Key Vault to create." + default = "standard" +} + +variable "kv_fw_default_action" { + type = string + default = "Deny" + description = "Default action for key vault firewall rules." + +} + +variable "kv_fw_bypass" { + type = string + default = "AzureServices" + description = "List of key vault firewall rules to bypass." +} + +variable "kv_fw_allowed_ips" { + type = list(string) + default = [] + description = "value of key vault firewall allowed ip rules." +} + +variable "kv_fw_network_subnet_ids" { + description = "The virtual network subnets to associate with the Cosmos DB account (Service Endpoint). If networking is created as part of the module, this will be automatically populated." + type = list(string) + default = null +} + +### 04 openai service ### +variable "oai_account_name" { + type = string + default = "az-openai-account" + description = "The name of the OpenAI service." +} + +variable "oai_sku_name" { + type = string + description = "SKU name of the OpenAI service." + default = "S0" +} + +variable "oai_custom_subdomain_name" { + type = string + description = "The subdomain name used for token-based authentication. Changing this forces a new resource to be created. (normally the same as the account name)" + default = "demo-account" +} + +variable "oai_dynamic_throttling_enabled" { + type = bool + default = true + description = "Whether or not dynamic throttling is enabled. Defaults to `true`." +} + +variable "oai_fqdns" { + type = list(string) + default = [] + description = "A list of FQDNs to be used for token-based authentication. Changing this forces a new resource to be created." +} + +variable "oai_local_auth_enabled" { + type = bool + default = true + description = "Whether local authentication methods is enabled for the Cognitive Account. Defaults to `true`." +} + +variable "oai_outbound_network_access_restricted" { + type = bool + default = false + description = "Whether or not outbound network access is restricted. Defaults to `false`." +} + +variable "oai_public_network_access_enabled" { + type = bool + default = false + description = "Whether or not public network access is enabled. Defaults to `false`." +} + +variable "oai_customer_managed_key" { + type = object({ + key_vault_key_id = string + identity_client_id = optional(string) + }) + default = null + description = <<-DESCRIPTION + type = object({ + key_vault_key_id = (Required) The ID of the Key Vault Key which should be used to Encrypt the data in this OpenAI Account. + identity_client_id = (Optional) The Client ID of the User Assigned Identity that has access to the key. This property only needs to be specified when there're multiple identities attached to the OpenAI Account. + }) + DESCRIPTION +} + +variable "oai_identity" { + type = object({ + type = string + identity_ids = optional(list(string)) + }) + default = { + type = "SystemAssigned" + } + description = <<-DESCRIPTION + type = object({ + type = (Required) The type of the Identity. Possible values are `SystemAssigned`, `UserAssigned`, `SystemAssigned, UserAssigned`. + identity_ids = (Optional) Specifies a list of User Assigned Managed Identity IDs to be assigned to this OpenAI Account. + }) + DESCRIPTION +} + +variable "oai_network_acls" { + type = set(object({ + default_action = string + ip_rules = optional(set(string)) + virtual_network_rules = optional(set(object({ + subnet_id = string + ignore_missing_vnet_service_endpoint = optional(bool, false) + }))) + })) + default = null + description = <<-DESCRIPTION + type = set(object({ + default_action = (Required) The Default Action to use when no rules match from ip_rules / virtual_network_rules. Possible values are `Allow` and `Deny`. + ip_rules = (Optional) One or more IP Addresses, or CIDR Blocks which should be able to access the Cognitive Account. + virtual_network_rules = optional(set(object({ + subnet_id = (Required) The ID of a Subnet which should be able to access the OpenAI Account. + ignore_missing_vnet_service_endpoint = (Optional) Whether ignore missing vnet service endpoint or not. Default to `false`. + }))) + })) + DESCRIPTION +} + +variable "oai_storage" { + type = list(object({ + storage_account_id = string + identity_client_id = optional(string) + })) + default = [] + description = <<-DESCRIPTION + type = list(object({ + storage_account_id = (Required) Full resource id of a Microsoft.Storage resource. + identity_client_id = (Optional) The client ID of the managed identity associated with the storage resource. + })) + DESCRIPTION + nullable = false +} + +variable "oai_model_deployment" { + type = list(object({ + deployment_id = string + model_name = string + model_format = string + model_version = string + scale_type = string + scale_tier = optional(string) + scale_size = optional(number) + scale_family = optional(string) + scale_capacity = optional(number) + rai_policy_name = optional(string) + })) + default = [] + description = <<-DESCRIPTION + type = list(object({ + deployment_id = (Required) The name of the Cognitive Services Account `Model Deployment`. Changing this forces a new resource to be created. + model_name = { + model_format = (Required) The format of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. Possible value is OpenAI. + model_name = (Required) The name of the Cognitive Services Account Deployment model. Changing this forces a new resource to be created. + model_version = (Required) The version of Cognitive Services Account Deployment model. + } + scale = { + scale_type = (Required) Deployment scale type. Possible value is Standard. Changing this forces a new resource to be created. + scale_tier = (Optional) Possible values are Free, Basic, Standard, Premium, Enterprise. Changing this forces a new resource to be created. + scale_size = (Optional) The SKU size. When the name field is the combination of tier and some other value, this would be the standalone code. Changing this forces a new resource to be created. + scale_family = (Optional) If the service has different generations of hardware, for the same SKU, then that can be captured here. Changing this forces a new resource to be created. + scale_capacity = (Optional) Tokens-per-Minute (TPM). If the SKU supports scale out/in then the capacity integer should be included. If scale out/in is not possible for the resource this may be omitted. Default value is 1. Changing this forces a new resource to be created. + } + rai_policy_name = (Optional) The name of RAI policy. Changing this forces a new resource to be created. + })) + DESCRIPTION + nullable = false +} + +### 05 OpenAI CosmosDB ### +variable "cosmosdb_name" { + description = "The name of the Cosmos DB account" + type = string + default = "openaicosmosdb" +} + +variable "cosmosdb_offer_type" { + description = "The offer type to use for the Cosmos DB account" + type = string + default = "Standard" +} + +variable "cosmosdb_kind" { + description = "The kind of Cosmos DB to create" + type = string + default = "MongoDB" +} + +variable "cosmosdb_automatic_failover" { + description = "Whether to enable automatic failover for the Cosmos DB account" + type = bool + default = false +} + +variable "use_cosmosdb_free_tier" { + description = "Whether to enable the free tier for the Cosmos DB account. This needs to be false if another instance already uses free tier." + type = bool + default = true +} + +variable "cosmosdb_consistency_level" { + description = "The consistency level of the Cosmos DB account" + type = string + default = "BoundedStaleness" +} + +variable "cosmosdb_max_interval_in_seconds" { + description = "The maximum staleness interval in seconds for the Cosmos DB account" + type = number + default = 10 +} + +variable "cosmosdb_max_staleness_prefix" { + description = "The maximum staleness prefix for the Cosmos DB account" + type = number + default = 200 +} + +variable "cosmosdb_geo_locations" { + description = "The geo-locations for the Cosmos DB account" + type = list(object({ + location = string + failover_priority = number + })) + default = [ + { + location = "uksouth" + failover_priority = 0 + } + ] +} + +variable "cosmosdb_capabilities" { + description = "The capabilities for the Cosmos DB account" + type = list(string) + default = ["EnableMongo", "MongoDBv3.4"] +} + +variable "cosmosdb_virtual_network_subnets" { + description = "The virtual network subnets to associate with the Cosmos DB account (Service Endpoint). If networking is created as part of the module, this will be automatically populated." + type = list(string) + default = null +} + +variable "cosmosdb_is_virtual_network_filter_enabled" { + description = "Whether to enable virtual network filtering for the Cosmos DB account" + type = bool + default = true +} + +variable "cosmosdb_public_network_access_enabled" { + description = "Whether to enable public network access for the Cosmos DB account" + type = bool + default = true +} + +### 06 app services (librechat app + meilisearch) ### +# App service Plan +variable "app_service_name" { + type = string + description = "Name of the Linux App Service Plan." + default = "openai-asp9000" +} + +variable "app_service_sku_name" { + type = string + description = "The SKU name of the App Service Plan." + default = "B1" +} + +# LibreChat App Service +variable "libre_app_name" { + type = string + description = "Name of the LibreChat App Service." + default = "librechatapp9000" +} + +variable "libre_app_public_network_access_enabled" { + type = bool + description = "Whether or not public network access is enabled. Defaults to `false`." + default = true +} + +variable "libre_app_virtual_network_subnet_id" { + type = string + description = "The ID of the subnet, used to allow access to the App Service (priority 100), e.g. cosmosdb, meilisearch etc. If networking is created as part of the module, this will be automatically populated if value is 'null'." + default = null +} + +variable "libre_app_allowed_subnets" { + description = "Allowed Subnets (By default the subnet the app service is deployed in is allowed access already as priority 100). Add any additionals here" + type = list(object({ + virtual_network_subnet_id = string + priority = number + name = string + action = string + })) + default = [ + { + virtual_network_subnet_id = "subnet_id1" + priority = 200 + name = "subnet-access-rule1" + action = "Allow" + } + ] +} + +variable "libre_app_allowed_ip_addresses" { + description = "Allowed IP Addresses. The CIDR notation of the IP or IP Range to match to allow. For example: 10.0.0.0/24 or 192.168.10.1/32" + type = list(object({ + ip_address = string + priority = number + name = string + action = string + })) + default = [ + { + ip_address = "0.0.0.0/0" # Allow all IP Addresses (change to your IP range) + priority = 300 + name = "ip-access-rule1" + action = "Allow" + } + ] +} + +# LibreChat App Service App Settings +# Server Config +variable "libre_app_title" { + type = string + description = "Add a custom title for the App." + default = "PrivateGPT" +} + +variable "libre_app_custom_footer" { + type = string + description = "Add a custom footer for the App." + default = "Privately hosted chat app powered by Azure OpenAI and LibreChat." +} + +variable "libre_app_host" { + type = string + description = "he server will listen to localhost:3080 by default. You can change the target IP as you want. If you want to make this server available externally, for example to share the server with others or expose this from a Docker container, set host to 0.0.0.0 or your external IP interface." + default = "0.0.0.0" +} + +variable "libre_app_port" { + type = number + description = "The host port to listen on." + default = 3080 +} + +variable "libre_app_docker_image" { + type = string + description = "The Docker Image to use for the App Service." + default = "ghcr.io/danny-avila/librechat-dev-api:latest" +} + +variable "libre_app_mongo_uri" { + type = string + description = "The MongoDB Connection String to connect to." + default = null + sensitive = true +} + +variable "libre_app_domain_client" { + type = string + description = "To use locally, set DOMAIN_CLIENT and DOMAIN_SERVER to http://localhost:3080 (3080 being the port previously configured).When deploying to a custom domain, set DOMAIN_CLIENT and DOMAIN_SERVER to your deployed URL, e.g. https://mydomain.example.com" + default = "http://localhost:3080" +} + +variable "libre_app_domain_server" { + type = string + description = "To use locally, set DOMAIN_CLIENT and DOMAIN_SERVER to http://localhost:3080 (3080 being the port previously configured).When deploying to a custom domain, set DOMAIN_CLIENT and DOMAIN_SERVER to your deployed URL, e.g. https://mydomain.example.com" + default = "http://localhost:3080" +} + +# Debug logging +variable "libre_app_debug_logging" { + type = bool + description = "LibreChat has central logging built into its backend (api). Log files are saved in /api/logs. Error logs are saved by default. Debug logs are enabled by default but can be turned off if not desired." + default = false +} + +variable "libre_app_debug_console" { + type = bool + description = "Enable verbose server output in the console, though it's not recommended due to high verbosity." + default = false +} + +# Endpoints +variable "libre_app_endpoints" { + type = string + description = "endpoints and models selection. E.g. 'openAI,azureOpenAI,bingAI,chatGPTBrowser,google,gptPlugins,anthropic'" + default = "azureOpenAI" +} + +# Azure OpenAI +variable "libre_app_az_oai_api_key" { + type = string + description = "Azure OpenAI API Key" + default = null + sensitive = true +} + +variable "libre_app_az_oai_models" { + type = string + description = "Azure OpenAI Models. E.g. 'gpt-4-1106-preview,gpt-4,gpt-3.5-turbo,gpt-3.5-turbo-1106,gpt-4-vision-preview'" + default = "gpt-4-1106-preview" +} + +variable "libre_app_az_oai_use_model_as_deployment_name" { + type = bool + description = "Azure OpenAI Use Model as Deployment Name" + default = true +} + +variable "libre_app_az_oai_instance_name" { + type = string + description = "Azure OpenAI Instance Name" + default = null +} + +variable "libre_app_az_oai_api_version" { + type = string + description = "Azure OpenAI API Version" + default = "2023-07-01-preview" +} + +variable "libre_app_az_oai_dall3_api_version" { + type = string + description = "Azure OpenAI DALL-E API Version" + default = "2023-12-01-preview" +} + +variable "libre_app_az_oai_dall3_deployment_name" { + type = string + description = "Azure OpenAI DALL-E Deployment Name" + default = "dall-e-3" +} + +# Plugins +variable "libre_app_debug_plugins" { + type = bool + description = "Enable debug mode for Libre App plugins." + default = false +} + +variable "libre_app_plugins_creds_key" { + type = string + description = "Libre App Plugins Creds Key" + default = null + sensitive = true +} + +variable "libre_app_plugins_creds_iv" { + type = string + description = "Libre App Plugins Creds IV" + default = null + sensitive = true +} + +# Search +variable "libre_app_enable_meilisearch" { + type = bool + description = "Enable Meilisearch" + default = false +} + +variable "libre_app_meili_key" { + type = string + description = "Meilisearch API Key" + default = null + sensitive = true +} + +# User Registration +variable "libre_app_allow_email_login" { + type = bool + description = "Allow Email Login" + default = true +} + +variable "libre_app_allow_registration" { + type = bool + description = "Allow Registration" + default = true +} + +variable "libre_app_allow_social_login" { + type = bool + description = "Allow Social Login" + default = false +} + +variable "libre_app_allow_social_registration" { + type = bool + description = "Allow Social Registration" + default = false +} + +variable "libre_app_jwt_secret" { + type = string + description = "JWT Secret" + default = null + sensitive = true +} + +variable "libre_app_jwt_refresh_secret" { + type = string + description = "JWT Refresh Secret" + default = null + sensitive = true +} + +# Violations +variable "libre_app_violations" { + description = "Configuration for violations" + type = object({ + enabled = bool + ban_duration = number + ban_interval = number + login_violation_score = number + registration_violation_score = number + concurrent_violation_score = number + message_violation_score = number + non_browser_violation_score = number + login_max = number + login_window = number + register_max = number + register_window = number + limit_concurrent_messages = bool + concurrent_message_max = number + limit_message_ip = bool + message_ip_max = number + message_ip_window = number + limit_message_user = bool + message_user_max = number + message_user_window = number + }) + default = { + enabled = true + ban_duration = 1000 * 60 * 60 * 2 + ban_interval = 20 + login_violation_score = 1 + registration_violation_score = 1 + concurrent_violation_score = 1 + message_violation_score = 1 + non_browser_violation_score = 20 + login_max = 7 + login_window = 5 + register_max = 5 + register_window = 60 + limit_concurrent_messages = true + concurrent_message_max = 2 + limit_message_ip = true + message_ip_max = 40 + message_ip_window = 1 + limit_message_user = false + message_user_max = 40 + message_user_window = 1 + } +} + +# Custom Domain and Managed Certificate (Optional) +variable "libre_app_custom_domain_create" { + type = bool + description = "Create a custom domain and managed certificate for the App Service." + default = false +} + +variable "librechat_app_custom_domain_name" { + type = string + description = "The custom domain to use for the App Service." + default = "privategpt" +} + +variable "librechat_app_custom_dns_zone_name" { + type = string + description = "The DNS Zone to use for the App Service." + default = "domain.com" +} + +variable "dns_resource_group_name" { + type = string + description = "The Resource Group that contains the custom DNS Zone to use for the App Service" + default = "dns-rg" +} \ No newline at end of file diff --git a/outputs.tf b/outputs.tf index 3b530d4..dd69d42 100644 --- a/outputs.tf +++ b/outputs.tf @@ -1,63 +1,138 @@ # ################################################# # # OUTPUTS # # ################################################# -# ## OpenAI Service Account Details -# output "openai_endpoint" { -# description = "The endpoint used to connect to the Cognitive Service Account." -# value = module.openai.openai_endpoint -# } - -# output "openai_primary_key" { -# description = "The primary access key for the Cognitive Service Account." -# sensitive = true -# value = module.openai.openai_primary_key -# } - -# output "openai_secondary_key" { -# description = "The secondary access key for the Cognitive Service Account." -# sensitive = true -# value = module.openai.openai_secondary_key -# } - -# output "openai_subdomain" { -# description = "The subdomain used to connect to the Cognitive Service Account." -# value = module.openai.openai_subdomain -# } - -# output "openai_account_name" { -# description = "The name of the Cognitive Service Account." -# value = module.openai.openai_account_name -# } - -# ## key vault -# output "key_vault_id" { -# description = "The ID of the Key Vault used to store OpenAI account and model details." -# value = module.openai.key_vault_id -# } - -# output "key_vault_uri" { -# description = "The URI of the Key Vault used to store OpenAI account and model details.." -# value = module.openai.key_vault_uri -# } - -# ## Container App Enviornment -# #output "container_app_enviornment_id" { -# # description = "The ID of the container app enviornment." -# # value = module.privategpt_chatbot_container_apps.container_app_environment_id -# #} - -# ## Container App -# #output "container_app_id" { -# # description = "The ID of the container app." -# # value = module.privategpt_chatbot_container_apps.container_app_id -# #} - -# #output "latest_revision_fqdn" { -# # description = "The FQDN of the Latest Revision of the Container App." -# # value = module.privategpt_chatbot_container_apps.latest_revision_fqdn -# #} - -# #output "latest_revision_name" { -# # description = "The Name of the Latest Revision of the Container App." -# # value = module.privategpt_chatbot_container_apps.latest_revision_name -# #} + +# Network Details +output "virtual_network_id" { + description = "The ID of the virtual network" + value = azurerm_virtual_network.az_openai_vnet.id +} + +output "virtual_network_name" { + description = "The name of the virtual network" + value = azurerm_virtual_network.az_openai_vnet.name +} + +output "subnet_id" { + description = "The ID of the subnet" + value = azurerm_subnet.az_openai_subnet.id +} + +output "subnet_name" { + description = "The name of the subnet" + value = azurerm_subnet.az_openai_subnet.name +} + +# Key Vault Details +output "key_vault_id" { + description = "The ID of the Key Vault" + value = azurerm_key_vault.az_openai_kv.id +} + +output "key_vault_name" { + description = "The name of the Key Vault" + value = azurerm_key_vault.az_openai_kv.name +} + +output "key_vault_uri" { + description = "The URI of the Key Vault" + value = azurerm_key_vault.az_openai_kv.uri +} + +# OPENAI Details +output "openai_endpoint" { + description = "The endpoint used to connect to the Cognitive Service Account." + value = azurerm_cognitive_account.az_openai.endpoint +} + +output "openai_primary_key" { + description = "The primary access key for the Cognitive Service Account." + sensitive = true + value = azurerm_cognitive_account.az_openai.primary_access_key +} + +output "openai_secondary_key" { + description = "The secondary access key for the Cognitive Service Account." + sensitive = true + value = azurerm_cognitive_account.az_openai.secondary_access_key +} + +output "openai_subdomain" { + description = "The subdomain used to connect to the Cognitive Service Account." + value = azurerm_cognitive_account.az_openai.custom_subdomain_name +} + +output "cognitive_deployment_ids" { + description = "The IDs of the OpenAI Cognitive Account Model Deployments" + value = { for key, deployment in azurerm_cognitive_deployment.az_openai_models : key => deployment.id } +} + +output "cognitive_deployment_names" { + description = "The names of the OpenAI Cognitive Account Model Deployments" + value = { for key, deployment in azurerm_cognitive_deployment.az_openai_models : key => deployment.name } +} + +# CosmosDB Details +output "cosmosdb_account_id" { + description = "The ID of the Cosmos DB account" + value = azurerm_cosmosdb_account.az_openai_mongodb.id +} + +output "cosmosdb_account_name" { + description = "The name of the Cosmos DB account" + value = azurerm_cosmosdb_account.az_openai_mongodb.name +} + +output "cosmosdb_account_endpoint" { + description = "The endpoint used to connect to the Cosmos DB account" + value = azurerm_cosmosdb_account.az_openai_mongodb.endpoint +} + +output "cosmosdb_account_primary_key" { + description = "The primary master key for the Cosmos DB account" + sensitive = true + value = azurerm_cosmosdb_account.az_openai_mongodb.primary_key +} + +output "cosmosdb_account_secondary_key" { + description = "The secondary master key for the Cosmos DB account" + sensitive = true + value = azurerm_cosmosdb_account.az_openai_mongodb.secondary_key +} + +output "cosmosdb_account_connection_strings" { + description = "The primary connection string for the Cosmos DB account" + sensitive = true + value = azurerm_cosmosdb_account.az_openai_mongodb.connection_strings +} + +# App Service Details +output "app_service_plan_id" { + description = "The ID of the App Service Plan" + value = azurerm_service_plan.az_openai_asp.id +} + +output "app_service_plan_name" { + description = "The name of the App Service Plan" + value = azurerm_service_plan.az_openai_asp.name +} + +output "app_service_id" { + description = "The ID of the App Service" + value = azurerm_linux_web_app.librechat.id +} + +output "app_service_name" { + description = "The name of the App Service" + value = azurerm_linux_web_app.librechat.name +} + +output "app_service_default_hostname" { + description = "The default hostname of the App Service" + value = azurerm_linux_web_app.librechat.default_hostname +} + +output "app_service_outbound_ip_addresses" { + description = "The outbound IP addresses of the App Service" + value = azurerm_linux_web_app.librechat.outbound_ip_addresses +} diff --git a/tests/auto_test1/main.tf b/tests/auto_test1/main.tf index 8bedbd6..d3f34f7 100644 --- a/tests/auto_test1/main.tf +++ b/tests/auto_test1/main.tf @@ -145,6 +145,9 @@ module "private-chatgpt-openai" { libre_app_jwt_secret = var.libre_app_jwt_secret libre_app_jwt_refresh_secret = var.libre_app_jwt_refresh_secret + # Violations + libre_app_violations = var.libre_app_violations + # Custom Domain and Managed Certificate (Optional) libre_app_custom_domain_create = var.libre_app_custom_domain_create librechat_app_custom_domain_name = "${var.librechat_app_custom_domain_name}${random_integer.number.result}" diff --git a/tests/auto_test1/testing.auto.tfvars b/tests/auto_test1/testing.auto.tfvars index 8606dde..9caa702 100644 --- a/tests/auto_test1/testing.auto.tfvars +++ b/tests/auto_test1/testing.auto.tfvars @@ -177,6 +177,30 @@ libre_app_allow_social_registration = false libre_app_jwt_secret = null libre_app_jwt_refresh_secret = null +# violations +libre_app_violations = { + enabled = false + ban_duration = 1000 * 60 * 60 * 2 + ban_interval = 20 + login_violation_score = 1 + registration_violation_score = 1 + concurrent_violation_score = 1 + message_violation_score = 1 + non_browser_violation_score = 20 + login_max = 7 + login_window = 5 + register_max = 5 + register_window = 60 + limit_concurrent_messages = false + concurrent_message_max = 2 + limit_message_ip = false + message_ip_max = 40 + message_ip_window = 1 + limit_message_user = false + message_user_max = 40 + message_user_window = 1 +} + # Custom Domain and Managed Certificate (Optional) libre_app_custom_domain_create = true librechat_app_custom_domain_name = "privategpt" diff --git a/tests/auto_test1/variables.tf b/tests/auto_test1/variables.tf index cc2399e..b591fc5 100644 --- a/tests/auto_test1/variables.tf +++ b/tests/auto_test1/variables.tf @@ -639,8 +639,56 @@ variable "libre_app_jwt_refresh_secret" { sensitive = true } -# Custom Domain and Managed Certificate (Optional) +# Violations +variable "libre_app_violations" { + description = "Configuration for violations" + type = object({ + enabled = bool + ban_duration = number + ban_interval = number + login_violation_score = number + registration_violation_score = number + concurrent_violation_score = number + message_violation_score = number + non_browser_violation_score = number + login_max = number + login_window = number + register_max = number + register_window = number + limit_concurrent_messages = bool + concurrent_message_max = number + limit_message_ip = bool + message_ip_max = number + message_ip_window = number + limit_message_user = bool + message_user_max = number + message_user_window = number + }) + default = { + enabled = true + ban_duration = 1000 * 60 * 60 * 2 + ban_interval = 20 + login_violation_score = 1 + registration_violation_score = 1 + concurrent_violation_score = 1 + message_violation_score = 1 + non_browser_violation_score = 20 + login_max = 7 + login_window = 5 + register_max = 5 + register_window = 60 + limit_concurrent_messages = true + concurrent_message_max = 2 + limit_message_ip = true + message_ip_max = 40 + message_ip_window = 1 + limit_message_user = false + message_user_max = 40 + message_user_window = 1 + } +} +# Custom Domain and Managed Certificate (Optional) variable "libre_app_custom_domain_create" { type = bool description = "Create a custom domain and managed certificate for the App Service." diff --git a/variables.tf b/variables.tf index bbf9c7c..f169575 100644 --- a/variables.tf +++ b/variables.tf @@ -646,6 +646,56 @@ variable "libre_app_jwt_refresh_secret" { sensitive = true } +# Violations + +variable "libre_app_violations" { + description = "Configuration for violations" + type = object({ + enabled = bool + ban_duration = number + ban_interval = number + login_violation_score = number + registration_violation_score = number + concurrent_violation_score = number + message_violation_score = number + non_browser_violation_score = number + login_max = number + login_window = number + register_max = number + register_window = number + limit_concurrent_messages = bool + concurrent_message_max = number + limit_message_ip = bool + message_ip_max = number + message_ip_window = number + limit_message_user = bool + message_user_max = number + message_user_window = number + }) + default = { + enabled = true + ban_duration = 1000 * 60 * 60 * 2 + ban_interval = 20 + login_violation_score = 1 + registration_violation_score = 1 + concurrent_violation_score = 1 + message_violation_score = 1 + non_browser_violation_score = 20 + login_max = 7 + login_window = 5 + register_max = 5 + register_window = 60 + limit_concurrent_messages = true + concurrent_message_max = 2 + limit_message_ip = true + message_ip_max = 40 + message_ip_window = 1 + limit_message_user = false + message_user_max = 40 + message_user_window = 1 + } +} + # Custom Domain and Managed Certificate (Optional) variable "libre_app_custom_domain_create" {