├── files ├── logo.png ├── function │ ├── .vscode │ │ └── extensions.json │ ├── host.json │ ├── GetDbConnection │ │ ├── function.json │ │ └── run.csx │ └── .gitignore ├── sql_create_user.sql ├── sql_setup.sql ├── openvpn_vm_install.sh ├── cert.pem ├── key.pem └── openvpn-install.sh ├── variables.tf ├── .gitignore ├── LICENSE ├── README.md └── main.tf /files/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SecuraBV/brokenbydesign-azure/HEAD/files/logo.png -------------------------------------------------------------------------------- /files/function/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-azuretools.vscode-azurefunctions" 4 | ] 5 | } -------------------------------------------------------------------------------- /files/sql_create_user.sql: -------------------------------------------------------------------------------- 1 | CREATE LOGIN DevOps WITH PASSWORD = 'SECURA{C0NN3CT10N_STR1NG}'; 2 | CREATE USER DevOps FOR LOGIN DevOps; 3 | GO 4 | -- Give rolls to user in sql_setup.sql because we can't do this in the 'master' database -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "default_location" { 2 | description = "" 3 | type = string 4 | default = "West Europe" 5 | } 6 | 7 | variable "vpn_username" { 8 | description = "" 9 | type = string 10 | default = "Employee23187" 11 | } 12 | -------------------------------------------------------------------------------- /files/function/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "applicationInsights": { 5 | "samplingSettings": { 6 | "isEnabled": true, 7 | "excludedTypes": "Request" 8 | } 9 | } 10 | }, 11 | "extensionBundle": { 12 | "id": "Microsoft.Azure.Functions.ExtensionBundle", 13 | "version": "[2.*, 3.0.0)" 14 | } 15 | } -------------------------------------------------------------------------------- /files/function/GetDbConnection/function.json: -------------------------------------------------------------------------------- 1 | { 2 | "bindings": [ 3 | { 4 | "authLevel": "function", 5 | "name": "req", 6 | "type": "httpTrigger", 7 | "direction": "in", 8 | "methods": [ 9 | "get" 10 | ] 11 | }, 12 | { 13 | "name": "$return", 14 | "type": "http", 15 | "direction": "out" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /files/sql_setup.sql: -------------------------------------------------------------------------------- 1 | USE securavulnerabledb 2 | GO 3 | CREATE TABLE vpn_employee_data ( 4 | vpn_username varchar(255), 5 | vpn_password varchar(255) 6 | ) 7 | GO 8 | INSERT INTO vpn_employee_data VALUES ('Employee23187', 'SECURA{VPN_CR3D3NT14LS}') 9 | GO 10 | CREATE LOGIN DevOps WITH PASSWORD = 'SECURA{C0NN3CT10N_STR1NG}'; 11 | CREATE USER DevOps FOR LOGIN DevOps; 12 | GO 13 | EXEC sp_addrolemember db_datareader, DevOps; 14 | GO -------------------------------------------------------------------------------- /files/openvpn_vm_install.sh: -------------------------------------------------------------------------------- 1 | curl -O "https://raw.githubusercontent.com/SecuraBV/brokenbydesign-azure/main/files/openvpn-install.sh" && chmod +x openvpn-install.sh && echo "SUDO_PASSWORD" | sudo -S sh -c 'export AUTO_INSTALL=y && export MENU_OPTION="1" && export CLIENT="VPN_USERNAME" && export PASS="2" && IPV6_SUPPORT=n && export PASSPHRASE="SECURA{VPN_CR3D3NT14LS}" && ./openvpn-install.sh && mv /root/VPN_USERNAME.ovpn /home/ADMIN_USERNAME/VPN_USERNAME.ovpn && exit 0' && exit 0 -------------------------------------------------------------------------------- /files/function/GetDbConnection/run.csx: -------------------------------------------------------------------------------- 1 | #r "Newtonsoft.Json" 2 | 3 | using System.Net; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.Extensions.Primitives; 6 | using Newtonsoft.Json; 7 | 8 | public static async Task Run(HttpRequest req, ILogger log) 9 | { 10 | return new OkObjectResult("Server=tcp:securavulnerableserver.database.windows.net,1433;Initial Catalog=securavulnerabledb;Persist Security Info=False;User ID=DevOps;Password=SECURA{C0NN3CT10N_STR1NG};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"); 11 | } -------------------------------------------------------------------------------- /files/function/.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | obj 3 | csx 4 | .vs 5 | edge 6 | Publish 7 | 8 | *.user 9 | *.suo 10 | *.cscfg 11 | *.Cache 12 | project.lock.json 13 | 14 | /packages 15 | /TestResults 16 | 17 | /tools/NuGet.exe 18 | /App_Data 19 | /secrets 20 | /data 21 | .secrets 22 | appsettings.json 23 | local.settings.json 24 | 25 | node_modules 26 | dist 27 | 28 | # Local python packages 29 | .python_packages/ 30 | 31 | # Python Environments 32 | .env 33 | .venv 34 | env/ 35 | venv/ 36 | ENV/ 37 | env.bak/ 38 | venv.bak/ 39 | 40 | # Byte-compiled / optimized / DLL files 41 | __pycache__/ 42 | *.py[cod] 43 | *$py.class -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.ovpn 2 | 3 | 4 | # Local .terraform directories 5 | **/.terraform/* 6 | 7 | # .tfstate files 8 | *.tfstate 9 | *.tfstate.* 10 | .terraform.lock.hcl 11 | 12 | # Crash log files 13 | crash.log 14 | crash.*.log 15 | 16 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 17 | # password, private keys, and other secrets. These should not be part of version 18 | # control as they are data points which are potentially sensitive and subject 19 | # to change depending on the environment. 20 | *.tfvars 21 | *.tfvars.json 22 | 23 | # Ignore override files as they are usually used to override resources locally and so 24 | # are not checked in 25 | override.tf 26 | override.tf.json 27 | *_override.tf 28 | *_override.tf.json 29 | 30 | # Include override files you do wish to add to version control using negated pattern 31 | # !example_override.tf 32 | 33 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 34 | # example: *tfplan* 35 | 36 | # Ignore CLI configuration files 37 | .terraformrc 38 | terraform.rc 39 | 40 | 41 | files/temp.pem -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Secura 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /files/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDHzCCAgegAwIBAgIUaEcJmhc/7DjCPPyZLV0fWO1gcz8wDQYJKoZIhvcNAQEL 3 | BQAwHzELMAkGA1UEBhMCTkwxEDAOBgNVBAoMB1NlY3VyYSAwHhcNMjIwMzIyMTQz 4 | NjU3WhcNMzIwMzE5MTQzNjU3WjAfMQswCQYDVQQGEwJOTDEQMA4GA1UECgwHU2Vj 5 | dXJhIDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMzepUaYNoTsuWSk 6 | Zb9pgbIJrMEAp9oJuIGNlMgOxMcTHVix6Scb9Q3odWwBE39BhImc+cy8y3OpeuRq 7 | ADo98RIlBv8HkVYdOubR84NZvD/ugZfWJMrNCYFzOrRLeY53OGGgIxVJessyem5l 8 | q/16WzH0I3oRY65vOiEdNYZs0ZGOObtIffxmasz/+UwHWVeyxo8K5SBYAgGxhRPy 9 | tPhQW3s2whpkgXCLIScwrkrHsJvWGtKHqwpgF6vKXdmqGmJcZljLVNeD673SXU0a 10 | uCchxAG4nbQLKOccKvH1A677dSWXOtJ/8S6nJ5S/mdFy9uSSr1Fw85wAtXfb9MmB 11 | UWdFjakCAwEAAaNTMFEwHQYDVR0OBBYEFA/amktAYKEtTAKc7DqKV9K7Cs2EMB8G 12 | A1UdIwQYMBaAFA/amktAYKEtTAKc7DqKV9K7Cs2EMA8GA1UdEwEB/wQFMAMBAf8w 13 | DQYJKoZIhvcNAQELBQADggEBAK5JzdK7q7eYegr93EA+mzm53+qCqdQtdzFOAEzA 14 | dGpQcV9LWKHxbiVMl8/QrGhlD5LNsLmNsrtWKbGAcHMPDPdwWCjsF7YALlou9Js9 15 | lCRwyHaUTIss6YtnRVrcQSsclVzCG5yJoX8PHskGtvzg8Bs1xsPM5H9qXvjUCQqU 16 | 9wYNe6QaCbpznNbewoqijLWkwDiHbmuxwBoTp8fFxjtFnBXYsf4oTPKFeBqIKq07 17 | SP9OM5Qp0jQLt54H1NcTNe+6Vw2/11ZH6NOiLgHBIK+50sCiYeh8d35t+PLrH64k 18 | 6CXu0gLU3UZeTR65rVpScRGLnS+fLCWgc0MzyuolbBLVYfw= 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Azure vulnerable application 2 | broken by design white 3 | 4 | A vulnerable Azure architecture that is online 24/7. 5 | The environment provides several flags that can be found by exploiting Azure vulnerabilities. 6 | 7 | ## Links 8 | - Link to tool is: https://brokenazure.cloud 9 | - File issues at: https://github.com/SecuraBV/brokenbydesign-azure/issues 10 | - Link to website source code is: https://github.com/SecuraBV/brokenbydesign-azure-website 11 | - Created by: https://www.secura.com/ 12 | 13 | ## Requirements for development 14 | - [Azure CLI installed and in your $PATH](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) 15 | - [Terraform installed and in your $PATH](https://www.terraform.io/downloads) 16 | - [SQL Command line installed and in your $PATH](https://docs.microsoft.com/en-us/sql/linux/sql-server-linux-setup-tools?view=sql-server-ver16) 17 | - [Azure Functions Core Tools installed and in your $PATH](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local) 18 | - [OpenVPN to complete the challenges](https://www.ovpn.com/en/guides/ubuntu-cli) 19 | - sshpass installed and in your $PATH 20 | 21 | ## Running the Terraform scripts 22 | 1. Login using the `az login` 23 | 2. Make sure the certificates (`files/key.pem` and `files/cert.pem`) are still valid, currently untill 2032. 24 | 3. Run `terraform init` to install required providers 25 | 4. Run `terraform plan` to see changes (You can also skip this step) 26 | 5. Run `terraform apply` to apply changes 27 | 6. If you want to destroy the environment, run `Terraform destroy` 28 | 29 | ## Notes 30 | - Certificate and key expire `Mar 19 14:36:57 2032 GMT` 31 | - Make sure `Security defaults` is `disabled` (otherwise MFA is required on the DevOps user) 32 | - Error: `The subscription is not registered to use namespace 'Microsoft.Sql'` please run `az provider register --namespace Microsoft.Sql` 33 | - Error: `The subscription is not registered to use namespace 'Microsoft.Web'` please run `az provider register --namespace Microsoft.Web` 34 | 35 | ## Issues / to do 36 | - DevOps user is able to modify own profile ex. password and MFA 37 | - Run a runbook script every hour to reset password and MFA 38 | - DevOps user may leak IP adresses, geo-locations, browser version and OS type in profile settings 39 | - Reset whole environment (or only user) to minimize leaked information 40 | - Maybe change cloudName (presented when logging in as service principal) to a flag? 41 | 42 | ## Creating new certificates 43 | Run `openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem`. 44 | Make sure to add the application-id and tenant-id to the cert.pem in format: 45 | ``` 46 | -----BEGIN AZURE_DETAILS----- 47 | Tenant id: TENANT_ID_HERE 48 | App-id: APP_ID_HERE 49 | -----END AZURE_DETAILS----- 50 | ``` 51 | 52 | -------------------------------------------------------------------------------- /files/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDHzCCAgegAwIBAgIUaEcJmhc/7DjCPPyZLV0fWO1gcz8wDQYJKoZIhvcNAQEL 3 | BQAwHzELMAkGA1UEBhMCTkwxEDAOBgNVBAoMB1NlY3VyYSAwHhcNMjIwMzIyMTQz 4 | NjU3WhcNMzIwMzE5MTQzNjU3WjAfMQswCQYDVQQGEwJOTDEQMA4GA1UECgwHU2Vj 5 | dXJhIDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMzepUaYNoTsuWSk 6 | Zb9pgbIJrMEAp9oJuIGNlMgOxMcTHVix6Scb9Q3odWwBE39BhImc+cy8y3OpeuRq 7 | ADo98RIlBv8HkVYdOubR84NZvD/ugZfWJMrNCYFzOrRLeY53OGGgIxVJessyem5l 8 | q/16WzH0I3oRY65vOiEdNYZs0ZGOObtIffxmasz/+UwHWVeyxo8K5SBYAgGxhRPy 9 | tPhQW3s2whpkgXCLIScwrkrHsJvWGtKHqwpgF6vKXdmqGmJcZljLVNeD673SXU0a 10 | uCchxAG4nbQLKOccKvH1A677dSWXOtJ/8S6nJ5S/mdFy9uSSr1Fw85wAtXfb9MmB 11 | UWdFjakCAwEAAaNTMFEwHQYDVR0OBBYEFA/amktAYKEtTAKc7DqKV9K7Cs2EMB8G 12 | A1UdIwQYMBaAFA/amktAYKEtTAKc7DqKV9K7Cs2EMA8GA1UdEwEB/wQFMAMBAf8w 13 | DQYJKoZIhvcNAQELBQADggEBAK5JzdK7q7eYegr93EA+mzm53+qCqdQtdzFOAEzA 14 | dGpQcV9LWKHxbiVMl8/QrGhlD5LNsLmNsrtWKbGAcHMPDPdwWCjsF7YALlou9Js9 15 | lCRwyHaUTIss6YtnRVrcQSsclVzCG5yJoX8PHskGtvzg8Bs1xsPM5H9qXvjUCQqU 16 | 9wYNe6QaCbpznNbewoqijLWkwDiHbmuxwBoTp8fFxjtFnBXYsf4oTPKFeBqIKq07 17 | SP9OM5Qp0jQLt54H1NcTNe+6Vw2/11ZH6NOiLgHBIK+50sCiYeh8d35t+PLrH64k 18 | 6CXu0gLU3UZeTR65rVpScRGLnS+fLCWgc0MzyuolbBLVYfw= 19 | -----END CERTIFICATE----- 20 | -----BEGIN PRIVATE KEY----- 21 | MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDM3qVGmDaE7Llk 22 | pGW/aYGyCazBAKfaCbiBjZTIDsTHEx1YseknG/UN6HVsARN/QYSJnPnMvMtzqXrk 23 | agA6PfESJQb/B5FWHTrm0fODWbw/7oGX1iTKzQmBczq0S3mOdzhhoCMVSXrLMnpu 24 | Zav9elsx9CN6EWOubzohHTWGbNGRjjm7SH38ZmrM//lMB1lXssaPCuUgWAIBsYUT 25 | 8rT4UFt7NsIaZIFwiyEnMK5Kx7Cb1hrSh6sKYBeryl3ZqhpiXGZYy1TXg+u90l1N 26 | GrgnIcQBuJ20CyjnHCrx9QOu+3UllzrSf/EupyeUv5nRcvbkkq9RcPOcALV32/TJ 27 | gVFnRY2pAgMBAAECggEBALBvetVOV32oxY1YS8xKWAj1bhMVtnj/8CeawCx/E5cC 28 | 7j4pkks9N5GPxjiKwLjSuwss5rEdUvY8WnsGk0WVfN0MiHbwlIkeSVDqNZbEnGxO 29 | wsr6ANurM2mJzF/jtD8ui58AI9a8XoVK5sfWxgVZ79oYkMka2scqQVytZCBt7Ro1 30 | YSrjxI0sMmVpC29tjzkraJrc9xlqzr/ioKR94d5PBbbicRzwuN5f2CcXFZSbAULM 31 | CDHG6raajvlL1aSNQG8Lkouudw1E+1mMM//KPpAjQvkFajt+ugCqmV3fyO8td1W7 32 | ndsDMz6TcRc5syQlsFtZ28R1ugR5AOt/Q2NOG51zZtECgYEA5T8E+85keSjTmkQ4 33 | SUYuFEWFTUBoYsZ644fonntKJz+uMUilf4PP5tYm85Idereq/Wz2MR7YFo+Dhe/C 34 | lUGfMOqp6jGbRO9gwrAYifc9psR86dlPsvwbg1+MkrreqjM+dQYE3BzVBPeIHIaz 35 | OR0S7oJRZiQKzxX6dSPN6yHMQYUCgYEA5MdaSOvS2yssFOWPSajZdM3uJDwvB8yI 36 | A0zTzq/t0xGp4pG4hbbA0DsrlwbW+DFjNmhTUjnGi2xNFuBs2zpZAfZcS70JRi4G 37 | 3NSGRbUg2x7xJd+eDtHLfcqVAWS21Qv6cNnjWkIi+zD1mh8kuNpz6hd3MH6lZfuz 38 | FdFIPBGQAtUCgYEAjnnDRCh7A14fXQJHJSsr2kd22JNODQ2kNKM0LMMdTBVk0pZ+ 39 | 3Shz3th77ueB0NIzwDunKtIrpKHfMS/Y9GCLaqB9p+LayFYqAfXl2mFB/NKje8cm 40 | pGvRQa3xtQPU/VzJ1Xs/K/nzXpnlCy2gV7+9E2UE6AFAgoH7XjA5e4hO5O0CgYEA 41 | qrrK+dhjhwP05bNa91F21uBHc+sl/d/5MN1Iw9ou1XE9IsQ0vDTiN4OwyAhmrNnO 42 | fG/mnlpXfPzZmtTo58HnYruDrVHpdeIrZOmFOsgtONkihW0X+189SSbBhESw3NUP 43 | lOBF9rmceXDUGKxdL0Z3cp8IZ7xbmnv37bQ8//brTfECgYB38BuP+2JifsqhCO4V 44 | QwqzeI6QhETFZNpwBMLua/J6B/beDYNQNylWNmUhMZ5UrL+ZE+q83nHb6OuQvwwm 45 | 2JfOQqPWC33oDLgfSCJM4kZIt2K3AIozYmoSbRaS1g5WEL/2Z39/Kg+qjz1cX1UG 46 | h3A+1OWJej8nKg9XTz25Ra1qzg== 47 | -----END PRIVATE KEY----- 48 | -----BEGIN AZURE_DETAILS----- 49 | Tenant id: TENANT_ID_HERE 50 | App-id: APP_ID_HERE 51 | -----END AZURE_DETAILS----- 52 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | data "azurerm_client_config" "current" {} 2 | data "azurerm_subscription" "current" {} 3 | 4 | provider "azurerm" { 5 | skip_provider_registration = true 6 | features { 7 | key_vault { 8 | recover_soft_deleted_key_vaults = false 9 | purge_soft_delete_on_destroy = true 10 | } 11 | } 12 | } 13 | 14 | resource "azurerm_resource_group" "vulnerable" { 15 | name = "SuperCompany" 16 | location = var.default_location 17 | } 18 | 19 | resource "azurerm_storage_account" "vuln_storage_account" { 20 | name = "supercompanystorage" 21 | resource_group_name = azurerm_resource_group.vulnerable.name 22 | location = var.default_location 23 | account_tier = "Standard" 24 | account_replication_type = "LRS" 25 | } 26 | 27 | resource "azurerm_storage_container" "vuln_storage_container" { 28 | name = "storagecontainer" 29 | storage_account_name = azurerm_storage_account.vuln_storage_account.name 30 | container_access_type = "container" 31 | } 32 | 33 | resource "azurerm_storage_blob" "website_logo" { 34 | name = "logo.png" 35 | storage_account_name = azurerm_storage_account.vuln_storage_account.name 36 | storage_container_name = azurerm_storage_container.vuln_storage_container.name 37 | type = "Block" 38 | source = "files/logo.png" 39 | } 40 | 41 | resource "azuread_application" "vuln_application" { 42 | display_name = "Very important and secure application" 43 | 44 | provisioner "local-exec" { 45 | command = "cat files/key.pem | sed -e 's/APP_ID_HERE/${azuread_application.vuln_application.client_id}/g' -e 's/TENANT_ID_HERE/${data.azurerm_client_config.current.tenant_id}/g' > files/temp.pem" 46 | } 47 | } 48 | 49 | resource "azuread_service_principal" "vuln_application" { 50 | client_id = azuread_application.vuln_application.client_id 51 | app_role_assignment_required = false 52 | owners = [data.azurerm_client_config.current.object_id] 53 | 54 | feature_tags { 55 | enterprise = true 56 | gallery = true 57 | } 58 | } 59 | 60 | resource "azuread_directory_role" "vuln_application" { 61 | display_name = "Directory readers" 62 | } 63 | 64 | resource "azuread_directory_role_assignment" "vuln_application" { 65 | principal_object_id = azuread_service_principal.vuln_application.id 66 | role_id = azuread_directory_role.vuln_application.id 67 | } 68 | 69 | resource "azuread_application_certificate" "vuln_application_cert" { 70 | application_id = azuread_application.vuln_application.id 71 | type = "AsymmetricX509Cert" 72 | value = file("files/cert.pem") 73 | end_date = "2032-03-14T14:36:57Z" 74 | } 75 | 76 | resource "azurerm_storage_blob" "private_key" { 77 | name = "SECURA{C3RT1F1C3T3}.pem" 78 | storage_account_name = azurerm_storage_account.vuln_storage_account.name 79 | storage_container_name = azurerm_storage_container.vuln_storage_container.name 80 | type = "Block" 81 | source = "files/temp.pem" 82 | } 83 | 84 | 85 | # Azure function stuff 86 | resource "azurerm_resource_group" "devops_function" { 87 | name = "azure-func-rg-secura" 88 | location = var.default_location 89 | } 90 | 91 | resource "azurerm_storage_account" "devops_storage_acc" { 92 | name = "securafuncstor" 93 | resource_group_name = azurerm_resource_group.devops_function.name 94 | location = azurerm_resource_group.devops_function.location 95 | account_tier = "Standard" 96 | account_replication_type = "LRS" 97 | } 98 | 99 | resource "azurerm_service_plan" "devops_service_plan" { 100 | name = "azure-functions-secura-sp" 101 | location = azurerm_resource_group.devops_function.location 102 | resource_group_name = azurerm_resource_group.devops_function.name 103 | os_type = "Windows" 104 | sku_name = "Y1" # https://medium.com/@geralexgr/function-apps-are-not-supported-in-free-and-shared-plans-please-choose-a-different-plan-7f6b18fdfcd0 105 | } 106 | 107 | resource "azurerm_windows_function_app" "devops_func_app" { 108 | name = "af-secura" 109 | location = azurerm_resource_group.devops_function.location 110 | resource_group_name = azurerm_resource_group.devops_function.name 111 | service_plan_id = azurerm_service_plan.devops_service_plan.id 112 | storage_account_name = azurerm_storage_account.devops_storage_acc.name 113 | storage_account_access_key = azurerm_storage_account.devops_storage_acc.primary_access_key 114 | 115 | site_config {} 116 | } 117 | 118 | resource "null_resource" "publish_function" { 119 | depends_on = [ 120 | azurerm_windows_function_app.devops_func_app 121 | ] 122 | 123 | provisioner "local-exec" { 124 | command = "cd files/function;sleep 60;func azure functionapp publish af-secura --force --dotnet" 125 | } 126 | } 127 | 128 | # Database stuff 129 | resource "azurerm_resource_group" "db" { 130 | name = "secura-db-rg2" 131 | location = var.default_location 132 | } 133 | 134 | resource "azurerm_storage_account" "db" { 135 | name = "securadbstorageacc" 136 | resource_group_name = azurerm_resource_group.db.name 137 | location = azurerm_resource_group.db.location 138 | account_tier = "Standard" 139 | account_replication_type = "LRS" 140 | } 141 | 142 | resource "random_password" "password" { 143 | length = 16 144 | special = true 145 | override_special = "#$%&*()-_=+[]{}<>:?" 146 | min_numeric = 3 147 | min_lower = 3 148 | min_upper = 3 149 | min_special = 1 150 | } 151 | 152 | resource "azurerm_mssql_server" "db" { 153 | name = "securavulnerableserver" 154 | resource_group_name = azurerm_resource_group.db.name 155 | location = azurerm_resource_group.db.location 156 | version = "12.0" 157 | administrator_login = "Th1sUs3rn4m3!sUnh4ck4bl3" 158 | administrator_login_password = random_password.password.result 159 | minimum_tls_version = "1.2" 160 | 161 | timeouts { 162 | create = "1h30m" 163 | read = "1h30m" 164 | update = "2h" 165 | delete = "30m" 166 | } 167 | } 168 | 169 | resource "azurerm_mssql_firewall_rule" "db" { 170 | name = "All Access" 171 | server_id = azurerm_mssql_server.db.id 172 | start_ip_address = "0.0.0.0" 173 | end_ip_address = "255.255.255.255" 174 | } 175 | 176 | resource "azurerm_mssql_database" "db" { 177 | depends_on = [ 178 | azurerm_mssql_firewall_rule.db # We need the firewall rule to be applied otherwise we cannot execute the sqlcmd's from the local machine 179 | ] 180 | 181 | name = "securavulnerabledb" 182 | server_id = azurerm_mssql_server.db.id 183 | collation = "SQL_Latin1_General_CP1_CI_AS" 184 | license_type = "LicenseIncluded" 185 | max_size_gb = 1 186 | sku_name = "Basic" 187 | 188 | provisioner "local-exec" { 189 | command = "sleep 60;sqlcmd -S securavulnerableserver.database.windows.net -U Th1sUs3rn4m3!sUnh4ck4bl3 -P '${random_password.password.result}' -d master -i files/sql_create_user.sql; sqlcmd -S securavulnerableserver.database.windows.net -U Th1sUs3rn4m3!sUnh4ck4bl3 -P '${random_password.password.result}' -d securavulnerabledb -i files/sql_setup.sql" 190 | } 191 | 192 | timeouts { 193 | create = "1h30m" 194 | read = "1h30m" 195 | update = "2h" 196 | delete = "30m" 197 | } 198 | } 199 | 200 | # Azure DevOps account 201 | resource "azuread_user" "vuln_devops_user" { 202 | user_principal_name = "devops@secvulnapp.onmicrosoft.com" 203 | display_name = "DevOps" 204 | password = "SECURA{D4F4ULT_P4SSW0RD}" 205 | disable_password_expiration = true 206 | disable_strong_password = true 207 | office_location = "Password temp changed to SECURA{D4F4ULT_P4SSW0RD}" 208 | } 209 | 210 | resource "azurerm_resource_group" "vpn_network" { 211 | name = "azure-vpn-rg-secura" 212 | location = var.default_location 213 | } 214 | 215 | resource "azurerm_role_definition" "devops_role_def" { 216 | name = "DevOps reader" 217 | description = "Allow DevOps users to read some resources for development purpose" 218 | scope = azurerm_resource_group.devops_function.id 219 | 220 | permissions { 221 | actions = [ 222 | "Microsoft.Web/sites/*", 223 | "Microsoft.HybridCompute/machines/*/read", 224 | "Microsoft.Compute/*/read", 225 | "Microsoft.Resources/subscriptions/resourceGroups/read", 226 | "Microsoft.Resources/deployments/*/read", 227 | "Microsoft.Network/*/read", 228 | "Microsoft.DesktopVirtualization/*/read" 229 | ] 230 | not_actions = [ 231 | "*/write", 232 | "*/delete", 233 | "microsoft.web/apimanagementaccounts/apis/connections/listsecrets/action", 234 | "Microsoft.Web/containerApps/listsecrets/action", 235 | "Microsoft.Web/staticSites/listsecrets/action", 236 | "microsoft.web/sites/config/snapshots/listsecrets/action", 237 | "microsoft.web/sites/slots/config/snapshots/listsecrets/action", 238 | "microsoft.web/sites/slots/functions/listsecrets/action", 239 | "microsoft.web/sites/functions/listsecrets/action", 240 | "microsoft.web/apimanagementaccounts/apis/connections/listconnectionkeys/action", 241 | "microsoft.web/serverfarms/firstpartyapps/keyvaultsettings/read", 242 | "microsoft.web/serverfarms/firstpartyapps/keyvaultsettings/write", 243 | "microsoft.web/connections/listConnectionKeys/action", 244 | "microsoft.web/connections/revokeConnectionKeys/action", 245 | "Microsoft.Web/staticSites/resetapikey/Action", 246 | "microsoft.web/sites/hostruntime/functions/keys/read", 247 | "Microsoft.Web/sites/hostruntime/host/_master/read", 248 | "microsoft.web/sites/slots/functions/listkeys/action", 249 | "microsoft.web/sites/slots/functions/keys/write", 250 | "microsoft.web/sites/slots/functions/keys/delete", 251 | "microsoft.web/sites/slots/host/listkeys/action", 252 | "microsoft.web/sites/slots/host/functionkeys/write", 253 | "microsoft.web/sites/slots/host/functionkeys/delete", 254 | "microsoft.web/sites/slots/host/systemkeys/write", 255 | "microsoft.web/sites/slots/host/systemkeys/delete", 256 | "microsoft.web/sites/host/listkeys/action", 257 | "microsoft.web/sites/host/functionkeys/write", 258 | "microsoft.web/sites/host/functionkeys/delete", 259 | "microsoft.web/sites/host/systemkeys/write", 260 | "microsoft.web/sites/host/systemkeys/delete", 261 | "microsoft.web/sites/functions/listkeys/action", 262 | "microsoft.web/sites/functions/keys/write", 263 | "microsoft.web/sites/functions/keys/delete", 264 | "microsoft.web/sites/functions/masterkey/read", 265 | "microsoft.web/sites/hybridconnectionnamespaces/relays/listkeys/action", 266 | "Microsoft.Web/sites/slots/config/list/Action" 267 | ] 268 | } 269 | 270 | assignable_scopes = [ 271 | azurerm_resource_group.devops_function.id, 272 | azurerm_resource_group.vpn_network.id 273 | ] 274 | } 275 | 276 | resource "azurerm_role_assignment" "devops_role_assignment" { 277 | scope = azurerm_resource_group.devops_function.id 278 | role_definition_id = split("|", azurerm_role_definition.devops_role_def.id)[0] # For some reason this is broken? See https://github.com/hashicorp/terraform-provider-azurerm/issues/8426 279 | principal_id = azuread_user.vuln_devops_user.id 280 | } 281 | 282 | 283 | # VPN stuff 284 | resource "azurerm_role_assignment" "vpn_network_role_assignment" { 285 | scope = azurerm_resource_group.vpn_network.id 286 | role_definition_id = split("|", azurerm_role_definition.devops_role_def.id)[0] # For some reason this is broken? See https://github.com/hashicorp/terraform-provider-azurerm/issues/8426 287 | principal_id = azuread_user.vuln_devops_user.id 288 | } 289 | 290 | resource "azurerm_virtual_network" "mainvn" { 291 | name = "vpn-virtual-network" 292 | resource_group_name = azurerm_resource_group.vpn_network.name 293 | location = var.default_location 294 | address_space = ["10.1.0.0/16"] 295 | } 296 | 297 | resource "azurerm_subnet" "mainsubnet" { 298 | name = "vpn-vn-subnet" 299 | resource_group_name = azurerm_resource_group.vpn_network.name 300 | virtual_network_name = azurerm_virtual_network.mainvn.name 301 | address_prefixes = ["10.1.2.0/24"] 302 | } 303 | 304 | resource "azurerm_public_ip" "vpn_public_ip" { 305 | name = "vpn-public-ip" 306 | resource_group_name = azurerm_resource_group.vpn_network.name 307 | location = var.default_location 308 | allocation_method = "Static" 309 | sku = "Standard" 310 | } 311 | 312 | resource "azurerm_network_security_group" "allow_all" { 313 | name = "allow-all-nsg" 314 | location = var.default_location 315 | resource_group_name = azurerm_resource_group.vpn_network.name 316 | 317 | security_rule { 318 | name = "AllowAllInbound" 319 | priority = 100 320 | direction = "Inbound" 321 | access = "Allow" 322 | protocol = "*" 323 | source_port_range = "*" 324 | destination_port_range = "*" 325 | source_address_prefix = "*" 326 | destination_address_prefix = "*" 327 | } 328 | 329 | security_rule { 330 | name = "AllowAllOutbound" 331 | priority = 200 332 | direction = "Outbound" 333 | access = "Allow" 334 | protocol = "*" 335 | source_port_range = "*" 336 | destination_port_range = "*" 337 | source_address_prefix = "*" 338 | destination_address_prefix = "*" 339 | } 340 | } 341 | 342 | resource "azurerm_network_interface" "vpn_network_interface" { 343 | name = "vpn-network-interface" 344 | location = var.default_location 345 | resource_group_name = azurerm_resource_group.vpn_network.name 346 | 347 | ip_configuration { 348 | name = "vpn-ip" 349 | subnet_id = azurerm_subnet.mainsubnet.id 350 | private_ip_address_allocation = "Static" 351 | private_ip_address = "10.1.2.4" 352 | public_ip_address_id = azurerm_public_ip.vpn_public_ip.id 353 | } 354 | } 355 | 356 | resource "azurerm_network_interface_security_group_association" "vpn_nic_assoc" { 357 | network_interface_id = azurerm_network_interface.vpn_network_interface.id 358 | network_security_group_id = azurerm_network_security_group.allow_all.id 359 | } 360 | 361 | 362 | resource "azurerm_network_interface" "website_network_interface" { 363 | name = "website-network-interface" 364 | location = var.default_location 365 | resource_group_name = azurerm_resource_group.vpn_network.name 366 | 367 | ip_configuration { 368 | name = "website-ip" 369 | subnet_id = azurerm_subnet.mainsubnet.id 370 | private_ip_address_allocation = "Static" 371 | private_ip_address = "10.1.2.5" 372 | } 373 | } 374 | 375 | 376 | resource "azurerm_linux_virtual_machine" "vpn_host" { 377 | name = "vpn-host-machine" 378 | resource_group_name = azurerm_resource_group.vpn_network.name 379 | location = var.default_location 380 | network_interface_ids = [azurerm_network_interface.vpn_network_interface.id] 381 | size = "Standard_B1ms" 382 | 383 | computer_name = "vpnmachine" 384 | admin_username = "vpnmachine" 385 | admin_password = random_password.password.result 386 | disable_password_authentication = false 387 | 388 | os_disk { 389 | name = "VPN-disk" 390 | caching = "ReadWrite" 391 | disk_size_gb = 127 392 | storage_account_type = "Standard_LRS" 393 | } 394 | 395 | source_image_reference { 396 | publisher = "canonical" 397 | offer = "0001-com-ubuntu-server-focal" 398 | sku = "20_04-lts-gen2" 399 | version = "latest" 400 | } 401 | } 402 | 403 | resource "azurerm_virtual_machine_extension" "setupopenvpn" { 404 | name = azurerm_linux_virtual_machine.vpn_host.name 405 | virtual_machine_id = azurerm_linux_virtual_machine.vpn_host.id 406 | publisher = "Microsoft.Azure.Extensions" 407 | type = "CustomScript" 408 | type_handler_version = "2.0" 409 | 410 | settings = jsonencode({ 411 | commandToExecute = replace(replace(replace(file("./files/openvpn_vm_install.sh"), "SUDO_PASSWORD", random_password.password.result), "VPN_USERNAME", var.vpn_username), "ADMIN_USERNAME", azurerm_linux_virtual_machine.vpn_host.admin_username) 412 | }) 413 | 414 | provisioner "local-exec" { 415 | command = "sshpass -p '${random_password.password.result}' scp -o StrictHostKeyChecking=accept-new ${azurerm_linux_virtual_machine.vpn_host.admin_username}@${azurerm_linux_virtual_machine.vpn_host.public_ip_address}:/home/${azurerm_linux_virtual_machine.vpn_host.admin_username}/${var.vpn_username}.ovpn ./files/${var.vpn_username}.ovpn" 416 | } 417 | } 418 | 419 | resource "azurerm_storage_blob" "ovpn_file" { 420 | name = "${var.vpn_username}.ovpn" 421 | storage_account_name = azurerm_storage_account.vuln_storage_account.name 422 | storage_container_name = azurerm_storage_container.vuln_storage_container.name 423 | type = "Block" 424 | source = "files/${var.vpn_username}.ovpn" 425 | 426 | depends_on = [ 427 | azurerm_virtual_machine_extension.setupopenvpn 428 | ] 429 | } 430 | 431 | 432 | resource "azurerm_linux_virtual_machine" "website_host" { 433 | name = "vpn-website-machine" 434 | resource_group_name = azurerm_resource_group.vpn_network.name 435 | location = var.default_location 436 | network_interface_ids = [azurerm_network_interface.website_network_interface.id] 437 | size = "Standard_B1ms" 438 | 439 | computer_name = "websitemachine" 440 | admin_username = "websitemachine" 441 | admin_password = random_password.password.result 442 | disable_password_authentication = false 443 | 444 | os_disk { 445 | name = "Website-disk" 446 | caching = "ReadWrite" 447 | disk_size_gb = 127 448 | storage_account_type = "Standard_LRS" 449 | } 450 | 451 | source_image_reference { 452 | publisher = "canonical" 453 | offer = "0001-com-ubuntu-server-focal" 454 | sku = "20_04-lts-gen2" 455 | version = "latest" 456 | } 457 | } 458 | 459 | resource "azurerm_virtual_machine_extension" "setupwebsite" { 460 | name = azurerm_linux_virtual_machine.website_host.name 461 | virtual_machine_id = azurerm_linux_virtual_machine.website_host.id 462 | publisher = "Microsoft.Azure.Extensions" 463 | type = "CustomScript" 464 | type_handler_version = "2.0" 465 | 466 | settings = jsonencode({ 467 | commandToExecute = "sudo apt-get update -y && sudo apt-get install nginx -y && sudo echo \"

SECURA{1NT3RN4L_HTML_W3BP4G3}

\" > /var/www/html/index.html" 468 | }) 469 | } 470 | -------------------------------------------------------------------------------- /files/openvpn-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # shellcheck disable=SC1091,SC2164,SC2034,SC1072,SC1073,SC1009 3 | 4 | # Secure OpenVPN server installer for Debian, Ubuntu, CentOS, Amazon Linux 2, Fedora, Oracle Linux 8, Arch Linux, Rocky Linux and AlmaLinux. 5 | # https://github.com/angristan/openvpn-install 6 | 7 | function isRoot() { 8 | if [ "$EUID" -ne 0 ]; then 9 | return 1 10 | fi 11 | } 12 | 13 | function tunAvailable() { 14 | if [ ! -e /dev/net/tun ]; then 15 | return 1 16 | fi 17 | } 18 | 19 | function checkOS() { 20 | if [[ -e /etc/debian_version ]]; then 21 | OS="debian" 22 | source /etc/os-release 23 | 24 | if [[ $ID == "debian" || $ID == "raspbian" ]]; then 25 | if [[ $VERSION_ID -lt 9 ]]; then 26 | echo "⚠️ Your version of Debian is not supported." 27 | echo "" 28 | echo "However, if you're using Debian >= 9 or unstable/testing then you can continue, at your own risk." 29 | echo "" 30 | until [[ $CONTINUE =~ (y|n) ]]; do 31 | read -rp "Continue? [y/n]: " -e CONTINUE 32 | done 33 | if [[ $CONTINUE == "n" ]]; then 34 | exit 1 35 | fi 36 | fi 37 | elif [[ $ID == "ubuntu" ]]; then 38 | OS="ubuntu" 39 | MAJOR_UBUNTU_VERSION=$(echo "$VERSION_ID" | cut -d '.' -f1) 40 | if [[ $MAJOR_UBUNTU_VERSION -lt 16 ]]; then 41 | echo "⚠️ Your version of Ubuntu is not supported." 42 | echo "" 43 | echo "However, if you're using Ubuntu >= 16.04 or beta, then you can continue, at your own risk." 44 | echo "" 45 | until [[ $CONTINUE =~ (y|n) ]]; do 46 | read -rp "Continue? [y/n]: " -e CONTINUE 47 | done 48 | if [[ $CONTINUE == "n" ]]; then 49 | exit 1 50 | fi 51 | fi 52 | fi 53 | elif [[ -e /etc/system-release ]]; then 54 | source /etc/os-release 55 | if [[ $ID == "fedora" || $ID_LIKE == "fedora" ]]; then 56 | OS="fedora" 57 | fi 58 | if [[ $ID == "centos" || $ID == "rocky" || $ID == "almalinux" ]]; then 59 | OS="centos" 60 | if [[ ! $VERSION_ID =~ (7|8) ]]; then 61 | echo "⚠️ Your version of CentOS is not supported." 62 | echo "" 63 | echo "The script only support CentOS 7 and CentOS 8." 64 | echo "" 65 | exit 1 66 | fi 67 | fi 68 | if [[ $ID == "ol" ]]; then 69 | OS="oracle" 70 | if [[ ! $VERSION_ID =~ (8) ]]; then 71 | echo "Your version of Oracle Linux is not supported." 72 | echo "" 73 | echo "The script only support Oracle Linux 8." 74 | exit 1 75 | fi 76 | fi 77 | if [[ $ID == "amzn" ]]; then 78 | OS="amzn" 79 | if [[ $VERSION_ID != "2" ]]; then 80 | echo "⚠️ Your version of Amazon Linux is not supported." 81 | echo "" 82 | echo "The script only support Amazon Linux 2." 83 | echo "" 84 | exit 1 85 | fi 86 | fi 87 | elif [[ -e /etc/arch-release ]]; then 88 | OS=arch 89 | else 90 | echo "Looks like you aren't running this installer on a Debian, Ubuntu, Fedora, CentOS, Amazon Linux 2, Oracle Linux 8 or Arch Linux system" 91 | exit 1 92 | fi 93 | } 94 | 95 | function initialCheck() { 96 | if ! isRoot; then 97 | echo "Sorry, you need to run this as root" 98 | exit 1 99 | fi 100 | if ! tunAvailable; then 101 | echo "TUN is not available" 102 | exit 1 103 | fi 104 | checkOS 105 | } 106 | 107 | function installUnbound() { 108 | # If Unbound isn't installed, install it 109 | if [[ ! -e /etc/unbound/unbound.conf ]]; then 110 | 111 | if [[ $OS =~ (debian|ubuntu) ]]; then 112 | apt-get install -y unbound 113 | 114 | # Configuration 115 | echo 'interface: 10.8.0.1 116 | access-control: 10.8.0.1/24 allow 117 | hide-identity: yes 118 | hide-version: yes 119 | use-caps-for-id: yes 120 | prefetch: yes' >>/etc/unbound/unbound.conf 121 | 122 | elif [[ $OS =~ (centos|amzn|oracle) ]]; then 123 | yum install -y unbound 124 | 125 | # Configuration 126 | sed -i 's|# interface: 0.0.0.0$|interface: 10.8.0.1|' /etc/unbound/unbound.conf 127 | sed -i 's|# access-control: 127.0.0.0/8 allow|access-control: 10.8.0.1/24 allow|' /etc/unbound/unbound.conf 128 | sed -i 's|# hide-identity: no|hide-identity: yes|' /etc/unbound/unbound.conf 129 | sed -i 's|# hide-version: no|hide-version: yes|' /etc/unbound/unbound.conf 130 | sed -i 's|use-caps-for-id: no|use-caps-for-id: yes|' /etc/unbound/unbound.conf 131 | 132 | elif [[ $OS == "fedora" ]]; then 133 | dnf install -y unbound 134 | 135 | # Configuration 136 | sed -i 's|# interface: 0.0.0.0$|interface: 10.8.0.1|' /etc/unbound/unbound.conf 137 | sed -i 's|# access-control: 127.0.0.0/8 allow|access-control: 10.8.0.1/24 allow|' /etc/unbound/unbound.conf 138 | sed -i 's|# hide-identity: no|hide-identity: yes|' /etc/unbound/unbound.conf 139 | sed -i 's|# hide-version: no|hide-version: yes|' /etc/unbound/unbound.conf 140 | sed -i 's|# use-caps-for-id: no|use-caps-for-id: yes|' /etc/unbound/unbound.conf 141 | 142 | elif [[ $OS == "arch" ]]; then 143 | pacman -Syu --noconfirm unbound 144 | 145 | # Get root servers list 146 | curl -o /etc/unbound/root.hints https://www.internic.net/domain/named.cache 147 | 148 | if [[ ! -f /etc/unbound/unbound.conf.old ]]; then 149 | mv /etc/unbound/unbound.conf /etc/unbound/unbound.conf.old 150 | fi 151 | 152 | echo 'server: 153 | use-syslog: yes 154 | do-daemonize: no 155 | username: "unbound" 156 | directory: "/etc/unbound" 157 | trust-anchor-file: trusted-key.key 158 | root-hints: root.hints 159 | interface: 10.8.0.1 160 | access-control: 10.8.0.1/24 allow 161 | port: 53 162 | num-threads: 2 163 | use-caps-for-id: yes 164 | harden-glue: yes 165 | hide-identity: yes 166 | hide-version: yes 167 | qname-minimisation: yes 168 | prefetch: yes' >/etc/unbound/unbound.conf 169 | fi 170 | 171 | # IPv6 DNS for all OS 172 | if [[ $IPV6_SUPPORT == 'y' ]]; then 173 | echo 'interface: fd42:42:42:42::1 174 | access-control: fd42:42:42:42::/112 allow' >>/etc/unbound/unbound.conf 175 | fi 176 | 177 | if [[ ! $OS =~ (fedora|centos|amzn|oracle) ]]; then 178 | # DNS Rebinding fix 179 | echo "private-address: 10.0.0.0/8 180 | private-address: fd42:42:42:42::/112 181 | private-address: 172.16.0.0/12 182 | private-address: 192.168.0.0/16 183 | private-address: 169.254.0.0/16 184 | private-address: fd00::/8 185 | private-address: fe80::/10 186 | private-address: 127.0.0.0/8 187 | private-address: ::ffff:0:0/96" >>/etc/unbound/unbound.conf 188 | fi 189 | else # Unbound is already installed 190 | echo 'include: /etc/unbound/openvpn.conf' >>/etc/unbound/unbound.conf 191 | 192 | # Add Unbound 'server' for the OpenVPN subnet 193 | echo 'server: 194 | interface: 10.8.0.1 195 | access-control: 10.8.0.1/24 allow 196 | hide-identity: yes 197 | hide-version: yes 198 | use-caps-for-id: yes 199 | prefetch: yes 200 | private-address: 10.0.0.0/8 201 | private-address: fd42:42:42:42::/112 202 | private-address: 172.16.0.0/12 203 | private-address: 192.168.0.0/16 204 | private-address: 169.254.0.0/16 205 | private-address: fd00::/8 206 | private-address: fe80::/10 207 | private-address: 127.0.0.0/8 208 | private-address: ::ffff:0:0/96' >/etc/unbound/openvpn.conf 209 | if [[ $IPV6_SUPPORT == 'y' ]]; then 210 | echo 'interface: fd42:42:42:42::1 211 | access-control: fd42:42:42:42::/112 allow' >>/etc/unbound/openvpn.conf 212 | fi 213 | fi 214 | 215 | systemctl enable unbound 216 | systemctl restart unbound 217 | } 218 | 219 | function installQuestions() { 220 | echo "Welcome to the OpenVPN installer!" 221 | echo "The git repository is available at: https://github.com/angristan/openvpn-install" 222 | echo "" 223 | 224 | echo "I need to ask you a few questions before starting the setup." 225 | echo "You can leave the default options and just press enter if you are ok with them." 226 | echo "" 227 | echo "I need to know the IPv4 address of the network interface you want OpenVPN listening to." 228 | echo "Unless your server is behind NAT, it should be your public IPv4 address." 229 | 230 | # Detect public IPv4 address and pre-fill for the user 231 | IP=$(ip -4 addr | sed -ne 's|^.* inet \([^/]*\)/.* scope global.*$|\1|p' | head -1) 232 | 233 | if [[ -z $IP ]]; then 234 | # Detect public IPv6 address 235 | IP=$(ip -6 addr | sed -ne 's|^.* inet6 \([^/]*\)/.* scope global.*$|\1|p' | head -1) 236 | fi 237 | APPROVE_IP=${APPROVE_IP:-n} 238 | if [[ $APPROVE_IP =~ n ]]; then 239 | read -rp "IP address: " -e -i "$IP" IP 240 | fi 241 | # If $IP is a private IP address, the server must be behind NAT 242 | if echo "$IP" | grep -qE '^(10\.|172\.1[6789]\.|172\.2[0-9]\.|172\.3[01]\.|192\.168)'; then 243 | echo "" 244 | echo "It seems this server is behind NAT. What is its public IPv4 address or hostname?" 245 | echo "We need it for the clients to connect to the server." 246 | 247 | PUBLICIP=$(curl -s https://api.ipify.org) 248 | until [[ $ENDPOINT != "" ]]; do 249 | read -rp "Public IPv4 address or hostname: " -e -i "$PUBLICIP" ENDPOINT 250 | done 251 | fi 252 | 253 | echo "" 254 | echo "Checking for IPv6 connectivity..." 255 | echo "" 256 | # "ping6" and "ping -6" availability varies depending on the distribution 257 | if type ping6 >/dev/null 2>&1; then 258 | PING6="ping6 -c3 ipv6.google.com > /dev/null 2>&1" 259 | else 260 | PING6="ping -6 -c3 ipv6.google.com > /dev/null 2>&1" 261 | fi 262 | if eval "$PING6"; then 263 | echo "Your host appears to have IPv6 connectivity." 264 | SUGGESTION="y" 265 | else 266 | echo "Your host does not appear to have IPv6 connectivity." 267 | SUGGESTION="n" 268 | fi 269 | echo "" 270 | # Ask the user if they want to enable IPv6 regardless its availability. 271 | until [[ $IPV6_SUPPORT =~ (y|n) ]]; do 272 | read -rp "Do you want to enable IPv6 support (NAT)? [y/n]: " -e -i $SUGGESTION IPV6_SUPPORT 273 | done 274 | echo "" 275 | echo "What port do you want OpenVPN to listen to?" 276 | echo " 1) Default: 1194" 277 | echo " 2) Custom" 278 | echo " 3) Random [49152-65535]" 279 | until [[ $PORT_CHOICE =~ ^[1-3]$ ]]; do 280 | read -rp "Port choice [1-3]: " -e -i 1 PORT_CHOICE 281 | done 282 | case $PORT_CHOICE in 283 | 1) 284 | PORT="1194" 285 | ;; 286 | 2) 287 | until [[ $PORT =~ ^[0-9]+$ ]] && [ "$PORT" -ge 1 ] && [ "$PORT" -le 65535 ]; do 288 | read -rp "Custom port [1-65535]: " -e -i 1194 PORT 289 | done 290 | ;; 291 | 3) 292 | # Generate random number within private ports range 293 | PORT=$(shuf -i49152-65535 -n1) 294 | echo "Random Port: $PORT" 295 | ;; 296 | esac 297 | echo "" 298 | echo "What protocol do you want OpenVPN to use?" 299 | echo "UDP is faster. Unless it is not available, you shouldn't use TCP." 300 | echo " 1) UDP" 301 | echo " 2) TCP" 302 | until [[ $PROTOCOL_CHOICE =~ ^[1-2]$ ]]; do 303 | read -rp "Protocol [1-2]: " -e -i 1 PROTOCOL_CHOICE 304 | done 305 | case $PROTOCOL_CHOICE in 306 | 1) 307 | PROTOCOL="udp" 308 | ;; 309 | 2) 310 | PROTOCOL="tcp" 311 | ;; 312 | esac 313 | echo "" 314 | echo "What DNS resolvers do you want to use with the VPN?" 315 | echo " 1) Current system resolvers (from /etc/resolv.conf)" 316 | echo " 2) Self-hosted DNS Resolver (Unbound)" 317 | echo " 3) Cloudflare (Anycast: worldwide)" 318 | echo " 4) Quad9 (Anycast: worldwide)" 319 | echo " 5) Quad9 uncensored (Anycast: worldwide)" 320 | echo " 6) FDN (France)" 321 | echo " 7) DNS.WATCH (Germany)" 322 | echo " 8) OpenDNS (Anycast: worldwide)" 323 | echo " 9) Google (Anycast: worldwide)" 324 | echo " 10) Yandex Basic (Russia)" 325 | echo " 11) AdGuard DNS (Anycast: worldwide)" 326 | echo " 12) NextDNS (Anycast: worldwide)" 327 | echo " 13) Custom" 328 | until [[ $DNS =~ ^[0-9]+$ ]] && [ "$DNS" -ge 1 ] && [ "$DNS" -le 13 ]; do 329 | read -rp "DNS [1-12]: " -e -i 11 DNS 330 | if [[ $DNS == 2 ]] && [[ -e /etc/unbound/unbound.conf ]]; then 331 | echo "" 332 | echo "Unbound is already installed." 333 | echo "You can allow the script to configure it in order to use it from your OpenVPN clients" 334 | echo "We will simply add a second server to /etc/unbound/unbound.conf for the OpenVPN subnet." 335 | echo "No changes are made to the current configuration." 336 | echo "" 337 | 338 | until [[ $CONTINUE =~ (y|n) ]]; do 339 | read -rp "Apply configuration changes to Unbound? [y/n]: " -e CONTINUE 340 | done 341 | if [[ $CONTINUE == "n" ]]; then 342 | # Break the loop and cleanup 343 | unset DNS 344 | unset CONTINUE 345 | fi 346 | elif [[ $DNS == "13" ]]; then 347 | until [[ $DNS1 =~ ^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$ ]]; do 348 | read -rp "Primary DNS: " -e DNS1 349 | done 350 | until [[ $DNS2 =~ ^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$ ]]; do 351 | read -rp "Secondary DNS (optional): " -e DNS2 352 | if [[ $DNS2 == "" ]]; then 353 | break 354 | fi 355 | done 356 | fi 357 | done 358 | echo "" 359 | echo "Do you want to use compression? It is not recommended since the VORACLE attack makes use of it." 360 | until [[ $COMPRESSION_ENABLED =~ (y|n) ]]; do 361 | read -rp"Enable compression? [y/n]: " -e -i n COMPRESSION_ENABLED 362 | done 363 | if [[ $COMPRESSION_ENABLED == "y" ]]; then 364 | echo "Choose which compression algorithm you want to use: (they are ordered by efficiency)" 365 | echo " 1) LZ4-v2" 366 | echo " 2) LZ4" 367 | echo " 3) LZ0" 368 | until [[ $COMPRESSION_CHOICE =~ ^[1-3]$ ]]; do 369 | read -rp"Compression algorithm [1-3]: " -e -i 1 COMPRESSION_CHOICE 370 | done 371 | case $COMPRESSION_CHOICE in 372 | 1) 373 | COMPRESSION_ALG="lz4-v2" 374 | ;; 375 | 2) 376 | COMPRESSION_ALG="lz4" 377 | ;; 378 | 3) 379 | COMPRESSION_ALG="lzo" 380 | ;; 381 | esac 382 | fi 383 | echo "" 384 | echo "Do you want to customize encryption settings?" 385 | echo "Unless you know what you're doing, you should stick with the default parameters provided by the script." 386 | echo "Note that whatever you choose, all the choices presented in the script are safe. (Unlike OpenVPN's defaults)" 387 | echo "See https://github.com/angristan/openvpn-install#security-and-encryption to learn more." 388 | echo "" 389 | until [[ $CUSTOMIZE_ENC =~ (y|n) ]]; do 390 | read -rp "Customize encryption settings? [y/n]: " -e -i n CUSTOMIZE_ENC 391 | done 392 | if [[ $CUSTOMIZE_ENC == "n" ]]; then 393 | # Use default, sane and fast parameters 394 | CIPHER="AES-128-GCM" 395 | CERT_TYPE="1" # ECDSA 396 | CERT_CURVE="prime256v1" 397 | CC_CIPHER="TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256" 398 | DH_TYPE="1" # ECDH 399 | DH_CURVE="prime256v1" 400 | HMAC_ALG="SHA256" 401 | TLS_SIG="1" # tls-crypt 402 | else 403 | echo "" 404 | echo "Choose which cipher you want to use for the data channel:" 405 | echo " 1) AES-128-GCM (recommended)" 406 | echo " 2) AES-192-GCM" 407 | echo " 3) AES-256-GCM" 408 | echo " 4) AES-128-CBC" 409 | echo " 5) AES-192-CBC" 410 | echo " 6) AES-256-CBC" 411 | until [[ $CIPHER_CHOICE =~ ^[1-6]$ ]]; do 412 | read -rp "Cipher [1-6]: " -e -i 1 CIPHER_CHOICE 413 | done 414 | case $CIPHER_CHOICE in 415 | 1) 416 | CIPHER="AES-128-GCM" 417 | ;; 418 | 2) 419 | CIPHER="AES-192-GCM" 420 | ;; 421 | 3) 422 | CIPHER="AES-256-GCM" 423 | ;; 424 | 4) 425 | CIPHER="AES-128-CBC" 426 | ;; 427 | 5) 428 | CIPHER="AES-192-CBC" 429 | ;; 430 | 6) 431 | CIPHER="AES-256-CBC" 432 | ;; 433 | esac 434 | echo "" 435 | echo "Choose what kind of certificate you want to use:" 436 | echo " 1) ECDSA (recommended)" 437 | echo " 2) RSA" 438 | until [[ $CERT_TYPE =~ ^[1-2]$ ]]; do 439 | read -rp"Certificate key type [1-2]: " -e -i 1 CERT_TYPE 440 | done 441 | case $CERT_TYPE in 442 | 1) 443 | echo "" 444 | echo "Choose which curve you want to use for the certificate's key:" 445 | echo " 1) prime256v1 (recommended)" 446 | echo " 2) secp384r1" 447 | echo " 3) secp521r1" 448 | until [[ $CERT_CURVE_CHOICE =~ ^[1-3]$ ]]; do 449 | read -rp"Curve [1-3]: " -e -i 1 CERT_CURVE_CHOICE 450 | done 451 | case $CERT_CURVE_CHOICE in 452 | 1) 453 | CERT_CURVE="prime256v1" 454 | ;; 455 | 2) 456 | CERT_CURVE="secp384r1" 457 | ;; 458 | 3) 459 | CERT_CURVE="secp521r1" 460 | ;; 461 | esac 462 | ;; 463 | 2) 464 | echo "" 465 | echo "Choose which size you want to use for the certificate's RSA key:" 466 | echo " 1) 2048 bits (recommended)" 467 | echo " 2) 3072 bits" 468 | echo " 3) 4096 bits" 469 | until [[ $RSA_KEY_SIZE_CHOICE =~ ^[1-3]$ ]]; do 470 | read -rp "RSA key size [1-3]: " -e -i 1 RSA_KEY_SIZE_CHOICE 471 | done 472 | case $RSA_KEY_SIZE_CHOICE in 473 | 1) 474 | RSA_KEY_SIZE="2048" 475 | ;; 476 | 2) 477 | RSA_KEY_SIZE="3072" 478 | ;; 479 | 3) 480 | RSA_KEY_SIZE="4096" 481 | ;; 482 | esac 483 | ;; 484 | esac 485 | echo "" 486 | echo "Choose which cipher you want to use for the control channel:" 487 | case $CERT_TYPE in 488 | 1) 489 | echo " 1) ECDHE-ECDSA-AES-128-GCM-SHA256 (recommended)" 490 | echo " 2) ECDHE-ECDSA-AES-256-GCM-SHA384" 491 | until [[ $CC_CIPHER_CHOICE =~ ^[1-2]$ ]]; do 492 | read -rp"Control channel cipher [1-2]: " -e -i 1 CC_CIPHER_CHOICE 493 | done 494 | case $CC_CIPHER_CHOICE in 495 | 1) 496 | CC_CIPHER="TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256" 497 | ;; 498 | 2) 499 | CC_CIPHER="TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384" 500 | ;; 501 | esac 502 | ;; 503 | 2) 504 | echo " 1) ECDHE-RSA-AES-128-GCM-SHA256 (recommended)" 505 | echo " 2) ECDHE-RSA-AES-256-GCM-SHA384" 506 | until [[ $CC_CIPHER_CHOICE =~ ^[1-2]$ ]]; do 507 | read -rp"Control channel cipher [1-2]: " -e -i 1 CC_CIPHER_CHOICE 508 | done 509 | case $CC_CIPHER_CHOICE in 510 | 1) 511 | CC_CIPHER="TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256" 512 | ;; 513 | 2) 514 | CC_CIPHER="TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384" 515 | ;; 516 | esac 517 | ;; 518 | esac 519 | echo "" 520 | echo "Choose what kind of Diffie-Hellman key you want to use:" 521 | echo " 1) ECDH (recommended)" 522 | echo " 2) DH" 523 | until [[ $DH_TYPE =~ [1-2] ]]; do 524 | read -rp"DH key type [1-2]: " -e -i 1 DH_TYPE 525 | done 526 | case $DH_TYPE in 527 | 1) 528 | echo "" 529 | echo "Choose which curve you want to use for the ECDH key:" 530 | echo " 1) prime256v1 (recommended)" 531 | echo " 2) secp384r1" 532 | echo " 3) secp521r1" 533 | while [[ $DH_CURVE_CHOICE != "1" && $DH_CURVE_CHOICE != "2" && $DH_CURVE_CHOICE != "3" ]]; do 534 | read -rp"Curve [1-3]: " -e -i 1 DH_CURVE_CHOICE 535 | done 536 | case $DH_CURVE_CHOICE in 537 | 1) 538 | DH_CURVE="prime256v1" 539 | ;; 540 | 2) 541 | DH_CURVE="secp384r1" 542 | ;; 543 | 3) 544 | DH_CURVE="secp521r1" 545 | ;; 546 | esac 547 | ;; 548 | 2) 549 | echo "" 550 | echo "Choose what size of Diffie-Hellman key you want to use:" 551 | echo " 1) 2048 bits (recommended)" 552 | echo " 2) 3072 bits" 553 | echo " 3) 4096 bits" 554 | until [[ $DH_KEY_SIZE_CHOICE =~ ^[1-3]$ ]]; do 555 | read -rp "DH key size [1-3]: " -e -i 1 DH_KEY_SIZE_CHOICE 556 | done 557 | case $DH_KEY_SIZE_CHOICE in 558 | 1) 559 | DH_KEY_SIZE="2048" 560 | ;; 561 | 2) 562 | DH_KEY_SIZE="3072" 563 | ;; 564 | 3) 565 | DH_KEY_SIZE="4096" 566 | ;; 567 | esac 568 | ;; 569 | esac 570 | echo "" 571 | # The "auth" options behaves differently with AEAD ciphers 572 | if [[ $CIPHER =~ CBC$ ]]; then 573 | echo "The digest algorithm authenticates data channel packets and tls-auth packets from the control channel." 574 | elif [[ $CIPHER =~ GCM$ ]]; then 575 | echo "The digest algorithm authenticates tls-auth packets from the control channel." 576 | fi 577 | echo "Which digest algorithm do you want to use for HMAC?" 578 | echo " 1) SHA-256 (recommended)" 579 | echo " 2) SHA-384" 580 | echo " 3) SHA-512" 581 | until [[ $HMAC_ALG_CHOICE =~ ^[1-3]$ ]]; do 582 | read -rp "Digest algorithm [1-3]: " -e -i 1 HMAC_ALG_CHOICE 583 | done 584 | case $HMAC_ALG_CHOICE in 585 | 1) 586 | HMAC_ALG="SHA256" 587 | ;; 588 | 2) 589 | HMAC_ALG="SHA384" 590 | ;; 591 | 3) 592 | HMAC_ALG="SHA512" 593 | ;; 594 | esac 595 | echo "" 596 | echo "You can add an additional layer of security to the control channel with tls-auth and tls-crypt" 597 | echo "tls-auth authenticates the packets, while tls-crypt authenticate and encrypt them." 598 | echo " 1) tls-crypt (recommended)" 599 | echo " 2) tls-auth" 600 | until [[ $TLS_SIG =~ [1-2] ]]; do 601 | read -rp "Control channel additional security mechanism [1-2]: " -e -i 1 TLS_SIG 602 | done 603 | fi 604 | echo "" 605 | echo "Okay, that was all I needed. We are ready to setup your OpenVPN server now." 606 | echo "You will be able to generate a client at the end of the installation." 607 | APPROVE_INSTALL=${APPROVE_INSTALL:-n} 608 | if [[ $APPROVE_INSTALL =~ n ]]; then 609 | read -n1 -r -p "Press any key to continue..." 610 | fi 611 | } 612 | 613 | function installOpenVPN() { 614 | if [[ $AUTO_INSTALL == "y" ]]; then 615 | # Set default choices so that no questions will be asked. 616 | APPROVE_INSTALL=${APPROVE_INSTALL:-y} 617 | APPROVE_IP=${APPROVE_IP:-y} 618 | IPV6_SUPPORT=${IPV6_SUPPORT:-n} 619 | PORT_CHOICE=${PORT_CHOICE:-1} 620 | PROTOCOL_CHOICE=${PROTOCOL_CHOICE:-1} 621 | DNS=${DNS:-1} 622 | COMPRESSION_ENABLED=${COMPRESSION_ENABLED:-n} 623 | CUSTOMIZE_ENC=${CUSTOMIZE_ENC:-n} 624 | CLIENT=${CLIENT:-client} 625 | PASS=${PASS:-1} 626 | CONTINUE=${CONTINUE:-y} 627 | 628 | # Behind NAT, we'll default to the publicly reachable IPv4/IPv6. 629 | if [[ $IPV6_SUPPORT == "y" ]]; then 630 | PUBLIC_IP=$(curl --retry 5 --retry-connrefused https://ip.seeip.org) 631 | else 632 | PUBLIC_IP=$(curl --retry 5 --retry-connrefused -4 https://ip.seeip.org) 633 | fi 634 | ENDPOINT=${ENDPOINT:-$PUBLIC_IP} 635 | fi 636 | 637 | # Run setup questions first, and set other variables if auto-install 638 | installQuestions 639 | 640 | # Get the "public" interface from the default route 641 | NIC=$(ip -4 route ls | grep default | grep -Po '(?<=dev )(\S+)' | head -1) 642 | if [[ -z $NIC ]] && [[ $IPV6_SUPPORT == 'y' ]]; then 643 | NIC=$(ip -6 route show default | sed -ne 's/^default .* dev \([^ ]*\) .*$/\1/p') 644 | fi 645 | 646 | # $NIC can not be empty for script rm-openvpn-rules.sh 647 | if [[ -z $NIC ]]; then 648 | echo 649 | echo "Can not detect public interface." 650 | echo "This needs for setup MASQUERADE." 651 | until [[ $CONTINUE =~ (y|n) ]]; do 652 | read -rp "Continue? [y/n]: " -e CONTINUE 653 | done 654 | if [[ $CONTINUE == "n" ]]; then 655 | exit 1 656 | fi 657 | fi 658 | 659 | # If OpenVPN isn't installed yet, install it. This script is more-or-less 660 | # idempotent on multiple runs, but will only install OpenVPN from upstream 661 | # the first time. 662 | if [[ ! -e /etc/openvpn/server.conf ]]; then 663 | if [[ $OS =~ (debian|ubuntu) ]]; then 664 | apt-get update 665 | apt-get -y install ca-certificates gnupg 666 | # We add the OpenVPN repo to get the latest version. 667 | if [[ $VERSION_ID == "16.04" ]]; then 668 | echo "deb http://build.openvpn.net/debian/openvpn/stable xenial main" >/etc/apt/sources.list.d/openvpn.list 669 | wget -O - https://swupdate.openvpn.net/repos/repo-public.gpg | apt-key add - 670 | apt-get update 671 | fi 672 | # Ubuntu > 16.04 and Debian > 8 have OpenVPN >= 2.4 without the need of a third party repository. 673 | apt-get install -y openvpn iptables openssl wget ca-certificates curl 674 | elif [[ $OS == 'centos' ]]; then 675 | yum install -y epel-release 676 | yum install -y openvpn iptables openssl wget ca-certificates curl tar 'policycoreutils-python*' 677 | elif [[ $OS == 'oracle' ]]; then 678 | yum install -y oracle-epel-release-el8 679 | yum-config-manager --enable ol8_developer_EPEL 680 | yum install -y openvpn iptables openssl wget ca-certificates curl tar policycoreutils-python-utils 681 | elif [[ $OS == 'amzn' ]]; then 682 | amazon-linux-extras install -y epel 683 | yum install -y openvpn iptables openssl wget ca-certificates curl 684 | elif [[ $OS == 'fedora' ]]; then 685 | dnf install -y openvpn iptables openssl wget ca-certificates curl policycoreutils-python-utils 686 | elif [[ $OS == 'arch' ]]; then 687 | # Install required dependencies and upgrade the system 688 | pacman --needed --noconfirm -Syu openvpn iptables openssl wget ca-certificates curl 689 | fi 690 | # An old version of easy-rsa was available by default in some openvpn packages 691 | if [[ -d /etc/openvpn/easy-rsa/ ]]; then 692 | rm -rf /etc/openvpn/easy-rsa/ 693 | fi 694 | fi 695 | 696 | # Find out if the machine uses nogroup or nobody for the permissionless group 697 | if grep -qs "^nogroup:" /etc/group; then 698 | NOGROUP=nogroup 699 | else 700 | NOGROUP=nobody 701 | fi 702 | 703 | # Install the latest version of easy-rsa from source, if not already installed. 704 | if [[ ! -d /etc/openvpn/easy-rsa/ ]]; then 705 | local version="3.0.7" 706 | wget -O ~/easy-rsa.tgz https://github.com/OpenVPN/easy-rsa/releases/download/v${version}/EasyRSA-${version}.tgz 707 | mkdir -p /etc/openvpn/easy-rsa 708 | tar xzf ~/easy-rsa.tgz --strip-components=1 --directory /etc/openvpn/easy-rsa 709 | rm -f ~/easy-rsa.tgz 710 | 711 | cd /etc/openvpn/easy-rsa/ || return 712 | case $CERT_TYPE in 713 | 1) 714 | echo "set_var EASYRSA_ALGO ec" >vars 715 | echo "set_var EASYRSA_CURVE $CERT_CURVE" >>vars 716 | ;; 717 | 2) 718 | echo "set_var EASYRSA_KEY_SIZE $RSA_KEY_SIZE" >vars 719 | ;; 720 | esac 721 | 722 | # Generate a random, alphanumeric identifier of 16 characters for CN and one for server name 723 | SERVER_CN="cn_$(head /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)" 724 | echo "$SERVER_CN" >SERVER_CN_GENERATED 725 | SERVER_NAME="server_$(head /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)" 726 | echo "$SERVER_NAME" >SERVER_NAME_GENERATED 727 | 728 | echo "set_var EASYRSA_REQ_CN $SERVER_CN" >>vars 729 | 730 | # Create the PKI, set up the CA, the DH params and the server certificate 731 | ./easyrsa init-pki 732 | ./easyrsa --batch build-ca nopass 733 | 734 | if [[ $DH_TYPE == "2" ]]; then 735 | # ECDH keys are generated on-the-fly so we don't need to generate them beforehand 736 | openssl dhparam -out dh.pem $DH_KEY_SIZE 737 | fi 738 | 739 | ./easyrsa build-server-full "$SERVER_NAME" nopass 740 | EASYRSA_CRL_DAYS=3650 ./easyrsa gen-crl 741 | 742 | case $TLS_SIG in 743 | 1) 744 | # Generate tls-crypt key 745 | openvpn --genkey --secret /etc/openvpn/tls-crypt.key 746 | ;; 747 | 2) 748 | # Generate tls-auth key 749 | openvpn --genkey --secret /etc/openvpn/tls-auth.key 750 | ;; 751 | esac 752 | else 753 | # If easy-rsa is already installed, grab the generated SERVER_NAME 754 | # for client configs 755 | cd /etc/openvpn/easy-rsa/ || return 756 | SERVER_NAME=$(cat SERVER_NAME_GENERATED) 757 | fi 758 | 759 | # Move all the generated files 760 | cp pki/ca.crt pki/private/ca.key "pki/issued/$SERVER_NAME.crt" "pki/private/$SERVER_NAME.key" /etc/openvpn/easy-rsa/pki/crl.pem /etc/openvpn 761 | if [[ $DH_TYPE == "2" ]]; then 762 | cp dh.pem /etc/openvpn 763 | fi 764 | 765 | # Make cert revocation list readable for non-root 766 | chmod 644 /etc/openvpn/crl.pem 767 | 768 | # Generate server.conf 769 | echo "port $PORT" >/etc/openvpn/server.conf 770 | if [[ $IPV6_SUPPORT == 'n' ]]; then 771 | echo "proto $PROTOCOL" >>/etc/openvpn/server.conf 772 | elif [[ $IPV6_SUPPORT == 'y' ]]; then 773 | echo "proto ${PROTOCOL}6" >>/etc/openvpn/server.conf 774 | fi 775 | 776 | echo "dev tun 777 | user nobody 778 | group $NOGROUP 779 | persist-key 780 | persist-tun 781 | keepalive 10 120 782 | topology subnet 783 | server 10.8.0.0 255.255.255.0 784 | ifconfig-pool-persist ipp.txt" >>/etc/openvpn/server.conf 785 | 786 | # DNS resolvers 787 | case $DNS in 788 | 1) # Current system resolvers 789 | # Locate the proper resolv.conf 790 | # Needed for systems running systemd-resolved 791 | if grep -q "127.0.0.53" "/etc/resolv.conf"; then 792 | RESOLVCONF='/run/systemd/resolve/resolv.conf' 793 | else 794 | RESOLVCONF='/etc/resolv.conf' 795 | fi 796 | # Obtain the resolvers from resolv.conf and use them for OpenVPN 797 | sed -ne 's/^nameserver[[:space:]]\+\([^[:space:]]\+\).*$/\1/p' $RESOLVCONF | while read -r line; do 798 | # Copy, if it's a IPv4 |or| if IPv6 is enabled, IPv4/IPv6 does not matter 799 | if [[ $line =~ ^[0-9.]*$ ]] || [[ $IPV6_SUPPORT == 'y' ]]; then 800 | echo "push \"dhcp-option DNS $line\"" >>/etc/openvpn/server.conf 801 | fi 802 | done 803 | ;; 804 | 2) # Self-hosted DNS resolver (Unbound) 805 | echo 'push "dhcp-option DNS 10.8.0.1"' >>/etc/openvpn/server.conf 806 | if [[ $IPV6_SUPPORT == 'y' ]]; then 807 | echo 'push "dhcp-option DNS fd42:42:42:42::1"' >>/etc/openvpn/server.conf 808 | fi 809 | ;; 810 | 3) # Cloudflare 811 | echo 'push "dhcp-option DNS 1.0.0.1"' >>/etc/openvpn/server.conf 812 | echo 'push "dhcp-option DNS 1.1.1.1"' >>/etc/openvpn/server.conf 813 | ;; 814 | 4) # Quad9 815 | echo 'push "dhcp-option DNS 9.9.9.9"' >>/etc/openvpn/server.conf 816 | echo 'push "dhcp-option DNS 149.112.112.112"' >>/etc/openvpn/server.conf 817 | ;; 818 | 5) # Quad9 uncensored 819 | echo 'push "dhcp-option DNS 9.9.9.10"' >>/etc/openvpn/server.conf 820 | echo 'push "dhcp-option DNS 149.112.112.10"' >>/etc/openvpn/server.conf 821 | ;; 822 | 6) # FDN 823 | echo 'push "dhcp-option DNS 80.67.169.40"' >>/etc/openvpn/server.conf 824 | echo 'push "dhcp-option DNS 80.67.169.12"' >>/etc/openvpn/server.conf 825 | ;; 826 | 7) # DNS.WATCH 827 | echo 'push "dhcp-option DNS 84.200.69.80"' >>/etc/openvpn/server.conf 828 | echo 'push "dhcp-option DNS 84.200.70.40"' >>/etc/openvpn/server.conf 829 | ;; 830 | 8) # OpenDNS 831 | echo 'push "dhcp-option DNS 208.67.222.222"' >>/etc/openvpn/server.conf 832 | echo 'push "dhcp-option DNS 208.67.220.220"' >>/etc/openvpn/server.conf 833 | ;; 834 | 9) # Google 835 | echo 'push "dhcp-option DNS 8.8.8.8"' >>/etc/openvpn/server.conf 836 | echo 'push "dhcp-option DNS 8.8.4.4"' >>/etc/openvpn/server.conf 837 | ;; 838 | 10) # Yandex Basic 839 | echo 'push "dhcp-option DNS 77.88.8.8"' >>/etc/openvpn/server.conf 840 | echo 'push "dhcp-option DNS 77.88.8.1"' >>/etc/openvpn/server.conf 841 | ;; 842 | 11) # AdGuard DNS 843 | echo 'push "dhcp-option DNS 94.140.14.14"' >>/etc/openvpn/server.conf 844 | echo 'push "dhcp-option DNS 94.140.15.15"' >>/etc/openvpn/server.conf 845 | ;; 846 | 12) # NextDNS 847 | echo 'push "dhcp-option DNS 45.90.28.167"' >>/etc/openvpn/server.conf 848 | echo 'push "dhcp-option DNS 45.90.30.167"' >>/etc/openvpn/server.conf 849 | ;; 850 | 13) # Custom DNS 851 | echo "push \"dhcp-option DNS $DNS1\"" >>/etc/openvpn/server.conf 852 | if [[ $DNS2 != "" ]]; then 853 | echo "push \"dhcp-option DNS $DNS2\"" >>/etc/openvpn/server.conf 854 | fi 855 | ;; 856 | esac 857 | echo 'push "redirect-gateway def1 bypass-dhcp"' >>/etc/openvpn/server.conf 858 | 859 | # IPv6 network settings if needed 860 | if [[ $IPV6_SUPPORT == 'y' ]]; then 861 | echo 'server-ipv6 fd42:42:42:42::/112 862 | tun-ipv6 863 | push tun-ipv6 864 | push "route-ipv6 2000::/3" 865 | push "redirect-gateway ipv6"' >>/etc/openvpn/server.conf 866 | fi 867 | 868 | if [[ $COMPRESSION_ENABLED == "y" ]]; then 869 | echo "compress $COMPRESSION_ALG" >>/etc/openvpn/server.conf 870 | fi 871 | 872 | if [[ $DH_TYPE == "1" ]]; then 873 | echo "dh none" >>/etc/openvpn/server.conf 874 | echo "ecdh-curve $DH_CURVE" >>/etc/openvpn/server.conf 875 | elif [[ $DH_TYPE == "2" ]]; then 876 | echo "dh dh.pem" >>/etc/openvpn/server.conf 877 | fi 878 | 879 | case $TLS_SIG in 880 | 1) 881 | echo "tls-crypt tls-crypt.key" >>/etc/openvpn/server.conf 882 | ;; 883 | 2) 884 | echo "tls-auth tls-auth.key 0" >>/etc/openvpn/server.conf 885 | ;; 886 | esac 887 | 888 | echo "crl-verify crl.pem 889 | ca ca.crt 890 | cert $SERVER_NAME.crt 891 | key $SERVER_NAME.key 892 | auth $HMAC_ALG 893 | cipher $CIPHER 894 | ncp-ciphers $CIPHER 895 | tls-server 896 | tls-version-min 1.2 897 | tls-cipher $CC_CIPHER 898 | client-config-dir /etc/openvpn/ccd 899 | status /var/log/openvpn/status.log 900 | verb 3" >>/etc/openvpn/server.conf 901 | 902 | # Create client-config-dir dir 903 | mkdir -p /etc/openvpn/ccd 904 | # Create log dir 905 | mkdir -p /var/log/openvpn 906 | 907 | # Enable routing 908 | echo 'net.ipv4.ip_forward=1' >/etc/sysctl.d/99-openvpn.conf 909 | if [[ $IPV6_SUPPORT == 'y' ]]; then 910 | echo 'net.ipv6.conf.all.forwarding=1' >>/etc/sysctl.d/99-openvpn.conf 911 | fi 912 | # Apply sysctl rules 913 | sysctl --system 914 | 915 | # If SELinux is enabled and a custom port was selected, we need this 916 | if hash sestatus 2>/dev/null; then 917 | if sestatus | grep "Current mode" | grep -qs "enforcing"; then 918 | if [[ $PORT != '1194' ]]; then 919 | semanage port -a -t openvpn_port_t -p "$PROTOCOL" "$PORT" 920 | fi 921 | fi 922 | fi 923 | 924 | # Finally, restart and enable OpenVPN 925 | if [[ $OS == 'arch' || $OS == 'fedora' || $OS == 'centos' || $OS == 'oracle' ]]; then 926 | # Don't modify package-provided service 927 | cp /usr/lib/systemd/system/openvpn-server@.service /etc/systemd/system/openvpn-server@.service 928 | 929 | # Workaround to fix OpenVPN service on OpenVZ 930 | sed -i 's|LimitNPROC|#LimitNPROC|' /etc/systemd/system/openvpn-server@.service 931 | # Another workaround to keep using /etc/openvpn/ 932 | sed -i 's|/etc/openvpn/server|/etc/openvpn|' /etc/systemd/system/openvpn-server@.service 933 | 934 | systemctl daemon-reload 935 | systemctl enable openvpn-server@server 936 | systemctl restart openvpn-server@server 937 | elif [[ $OS == "ubuntu" ]] && [[ $VERSION_ID == "16.04" ]]; then 938 | # On Ubuntu 16.04, we use the package from the OpenVPN repo 939 | # This package uses a sysvinit service 940 | systemctl enable openvpn 941 | systemctl start openvpn 942 | else 943 | # Don't modify package-provided service 944 | cp /lib/systemd/system/openvpn\@.service /etc/systemd/system/openvpn\@.service 945 | 946 | # Workaround to fix OpenVPN service on OpenVZ 947 | sed -i 's|LimitNPROC|#LimitNPROC|' /etc/systemd/system/openvpn\@.service 948 | # Another workaround to keep using /etc/openvpn/ 949 | sed -i 's|/etc/openvpn/server|/etc/openvpn|' /etc/systemd/system/openvpn\@.service 950 | 951 | systemctl daemon-reload 952 | systemctl enable openvpn@server 953 | systemctl restart openvpn@server 954 | fi 955 | 956 | if [[ $DNS == 2 ]]; then 957 | installUnbound 958 | fi 959 | 960 | # Add iptables rules in two scripts 961 | mkdir -p /etc/iptables 962 | 963 | # Script to add rules 964 | echo "#!/bin/sh 965 | iptables -t nat -I POSTROUTING 1 -s 10.8.0.0/24 -o $NIC -j MASQUERADE 966 | iptables -I INPUT 1 -i tun0 -j ACCEPT 967 | iptables -I FORWARD 1 -i $NIC -o tun0 -j ACCEPT 968 | iptables -I FORWARD 1 -i tun0 -o $NIC -j ACCEPT 969 | iptables -I INPUT 1 -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >/etc/iptables/add-openvpn-rules.sh 970 | 971 | if [[ $IPV6_SUPPORT == 'y' ]]; then 972 | echo "ip6tables -t nat -I POSTROUTING 1 -s fd42:42:42:42::/112 -o $NIC -j MASQUERADE 973 | ip6tables -I INPUT 1 -i tun0 -j ACCEPT 974 | ip6tables -I FORWARD 1 -i $NIC -o tun0 -j ACCEPT 975 | ip6tables -I FORWARD 1 -i tun0 -o $NIC -j ACCEPT 976 | ip6tables -I INPUT 1 -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >>/etc/iptables/add-openvpn-rules.sh 977 | fi 978 | 979 | # Script to remove rules 980 | echo "#!/bin/sh 981 | iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o $NIC -j MASQUERADE 982 | iptables -D INPUT -i tun0 -j ACCEPT 983 | iptables -D FORWARD -i $NIC -o tun0 -j ACCEPT 984 | iptables -D FORWARD -i tun0 -o $NIC -j ACCEPT 985 | iptables -D INPUT -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >/etc/iptables/rm-openvpn-rules.sh 986 | 987 | if [[ $IPV6_SUPPORT == 'y' ]]; then 988 | echo "ip6tables -t nat -D POSTROUTING -s fd42:42:42:42::/112 -o $NIC -j MASQUERADE 989 | ip6tables -D INPUT -i tun0 -j ACCEPT 990 | ip6tables -D FORWARD -i $NIC -o tun0 -j ACCEPT 991 | ip6tables -D FORWARD -i tun0 -o $NIC -j ACCEPT 992 | ip6tables -D INPUT -i $NIC -p $PROTOCOL --dport $PORT -j ACCEPT" >>/etc/iptables/rm-openvpn-rules.sh 993 | fi 994 | 995 | chmod +x /etc/iptables/add-openvpn-rules.sh 996 | chmod +x /etc/iptables/rm-openvpn-rules.sh 997 | 998 | # Handle the rules via a systemd script 999 | echo "[Unit] 1000 | Description=iptables rules for OpenVPN 1001 | Before=network-online.target 1002 | Wants=network-online.target 1003 | 1004 | [Service] 1005 | Type=oneshot 1006 | ExecStart=/etc/iptables/add-openvpn-rules.sh 1007 | ExecStop=/etc/iptables/rm-openvpn-rules.sh 1008 | RemainAfterExit=yes 1009 | 1010 | [Install] 1011 | WantedBy=multi-user.target" >/etc/systemd/system/iptables-openvpn.service 1012 | 1013 | # Enable service and apply rules 1014 | systemctl daemon-reload 1015 | systemctl enable iptables-openvpn 1016 | systemctl start iptables-openvpn 1017 | 1018 | # If the server is behind a NAT, use the correct IP address for the clients to connect to 1019 | if [[ $ENDPOINT != "" ]]; then 1020 | IP=$ENDPOINT 1021 | fi 1022 | 1023 | # client-template.txt is created so we have a template to add further users later 1024 | echo "client" >/etc/openvpn/client-template.txt 1025 | if [[ $PROTOCOL == 'udp' ]]; then 1026 | echo "proto udp" >>/etc/openvpn/client-template.txt 1027 | echo "explicit-exit-notify" >>/etc/openvpn/client-template.txt 1028 | elif [[ $PROTOCOL == 'tcp' ]]; then 1029 | echo "proto tcp-client" >>/etc/openvpn/client-template.txt 1030 | fi 1031 | echo "remote $IP $PORT 1032 | dev tun 1033 | resolv-retry infinite 1034 | nobind 1035 | persist-key 1036 | persist-tun 1037 | remote-cert-tls server 1038 | verify-x509-name $SERVER_NAME name 1039 | auth $HMAC_ALG 1040 | auth-nocache 1041 | cipher $CIPHER 1042 | tls-client 1043 | tls-version-min 1.2 1044 | tls-cipher $CC_CIPHER 1045 | ignore-unknown-option block-outside-dns 1046 | setenv opt block-outside-dns # Prevent Windows 10 DNS leak 1047 | verb 3" >>/etc/openvpn/client-template.txt 1048 | 1049 | if [[ $COMPRESSION_ENABLED == "y" ]]; then 1050 | echo "compress $COMPRESSION_ALG" >>/etc/openvpn/client-template.txt 1051 | fi 1052 | 1053 | # Generate the custom client.ovpn 1054 | newClient 1055 | echo "If you want to add more clients, you simply need to run this script another time!" 1056 | } 1057 | 1058 | function newClient() { 1059 | echo "" 1060 | echo "Tell me a name for the client." 1061 | echo "The name must consist of alphanumeric character. It may also include an underscore or a dash." 1062 | 1063 | until [[ $CLIENT =~ ^[a-zA-Z0-9_-]+$ ]]; do 1064 | read -rp "Client name: " -e CLIENT 1065 | done 1066 | 1067 | echo "" 1068 | echo "Do you want to protect the configuration file with a password?" 1069 | echo "(e.g. encrypt the private key with a password)" 1070 | echo " 1) Add a passwordless client" 1071 | echo " 2) Use a password for the client" 1072 | 1073 | until [[ $PASS =~ ^[1-2]$ ]]; do 1074 | read -rp "Select an option [1-2]: " -e -i 1 PASS 1075 | done 1076 | 1077 | CLIENTEXISTS=$(tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep -c -E "/CN=$CLIENT\$") 1078 | if [[ $CLIENTEXISTS == '1' ]]; then 1079 | echo "" 1080 | echo "The specified client CN was already found in easy-rsa, please choose another name." 1081 | exit 1082 | else 1083 | cd /etc/openvpn/easy-rsa/ || return 1084 | case $PASS in 1085 | 1) 1086 | ./easyrsa build-client-full "$CLIENT" nopass 1087 | ;; 1088 | 2) 1089 | if [[ -z "$PASSPHRASE" ]]; then 1090 | echo "⚠️ You will be asked for the client password below ⚠️" 1091 | ./easyrsa build-client-full "$CLIENT" 1092 | else 1093 | echo "The password for client was already set" 1094 | PASSPHRASE="pass:${PASSPHRASE}" 1095 | ./easyrsa --passin=$PASSPHRASE --passout=$PASSPHRASE build-client-full "$CLIENT" 1096 | 1097 | fi 1098 | ;; 1099 | esac 1100 | echo "Client $CLIENT added." 1101 | fi 1102 | 1103 | # Home directory of the user, where the client configuration will be written 1104 | if [ -e "/home/${CLIENT}" ]; then 1105 | # if $1 is a user name 1106 | homeDir="/home/${CLIENT}" 1107 | elif [ "${SUDO_USER}" ]; then 1108 | # if not, use SUDO_USER 1109 | if [ "${SUDO_USER}" == "root" ]; then 1110 | # If running sudo as root 1111 | homeDir="/root" 1112 | else 1113 | homeDir="/home/${SUDO_USER}" 1114 | fi 1115 | else 1116 | # if not SUDO_USER, use /root 1117 | homeDir="/root" 1118 | fi 1119 | 1120 | # Determine if we use tls-auth or tls-crypt 1121 | if grep -qs "^tls-crypt" /etc/openvpn/server.conf; then 1122 | TLS_SIG="1" 1123 | elif grep -qs "^tls-auth" /etc/openvpn/server.conf; then 1124 | TLS_SIG="2" 1125 | fi 1126 | 1127 | # Generates the custom client.ovpn 1128 | cp /etc/openvpn/client-template.txt "$homeDir/$CLIENT.ovpn" 1129 | { 1130 | echo "" 1131 | cat "/etc/openvpn/easy-rsa/pki/ca.crt" 1132 | echo "" 1133 | 1134 | echo "" 1135 | awk '/BEGIN/,/END/' "/etc/openvpn/easy-rsa/pki/issued/$CLIENT.crt" 1136 | echo "" 1137 | 1138 | echo "" 1139 | cat "/etc/openvpn/easy-rsa/pki/private/$CLIENT.key" 1140 | echo "" 1141 | 1142 | case $TLS_SIG in 1143 | 1) 1144 | echo "" 1145 | cat /etc/openvpn/tls-crypt.key 1146 | echo "" 1147 | ;; 1148 | 2) 1149 | echo "key-direction 1" 1150 | echo "" 1151 | cat /etc/openvpn/tls-auth.key 1152 | echo "" 1153 | ;; 1154 | esac 1155 | } >>"$homeDir/$CLIENT.ovpn" 1156 | 1157 | echo "" 1158 | echo "The configuration file has been written to $homeDir/$CLIENT.ovpn." 1159 | echo "Download the .ovpn file and import it in your OpenVPN client." 1160 | 1161 | exit 0 1162 | } 1163 | 1164 | function revokeClient() { 1165 | NUMBEROFCLIENTS=$(tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep -c "^V") 1166 | if [[ $NUMBEROFCLIENTS == '0' ]]; then 1167 | echo "" 1168 | echo "You have no existing clients!" 1169 | exit 1 1170 | fi 1171 | 1172 | echo "" 1173 | echo "Select the existing client certificate you want to revoke" 1174 | tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep "^V" | cut -d '=' -f 2 | nl -s ') ' 1175 | until [[ $CLIENTNUMBER -ge 1 && $CLIENTNUMBER -le $NUMBEROFCLIENTS ]]; do 1176 | if [[ $CLIENTNUMBER == '1' ]]; then 1177 | read -rp "Select one client [1]: " CLIENTNUMBER 1178 | else 1179 | read -rp "Select one client [1-$NUMBEROFCLIENTS]: " CLIENTNUMBER 1180 | fi 1181 | done 1182 | CLIENT=$(tail -n +2 /etc/openvpn/easy-rsa/pki/index.txt | grep "^V" | cut -d '=' -f 2 | sed -n "$CLIENTNUMBER"p) 1183 | cd /etc/openvpn/easy-rsa/ || return 1184 | ./easyrsa --batch revoke "$CLIENT" 1185 | EASYRSA_CRL_DAYS=3650 ./easyrsa gen-crl 1186 | rm -f /etc/openvpn/crl.pem 1187 | cp /etc/openvpn/easy-rsa/pki/crl.pem /etc/openvpn/crl.pem 1188 | chmod 644 /etc/openvpn/crl.pem 1189 | find /home/ -maxdepth 2 -name "$CLIENT.ovpn" -delete 1190 | rm -f "/root/$CLIENT.ovpn" 1191 | sed -i "/^$CLIENT,.*/d" /etc/openvpn/ipp.txt 1192 | cp /etc/openvpn/easy-rsa/pki/index.txt{,.bk} 1193 | 1194 | echo "" 1195 | echo "Certificate for client $CLIENT revoked." 1196 | } 1197 | 1198 | function removeUnbound() { 1199 | # Remove OpenVPN-related config 1200 | sed -i '/include: \/etc\/unbound\/openvpn.conf/d' /etc/unbound/unbound.conf 1201 | rm /etc/unbound/openvpn.conf 1202 | 1203 | until [[ $REMOVE_UNBOUND =~ (y|n) ]]; do 1204 | echo "" 1205 | echo "If you were already using Unbound before installing OpenVPN, I removed the configuration related to OpenVPN." 1206 | read -rp "Do you want to completely remove Unbound? [y/n]: " -e REMOVE_UNBOUND 1207 | done 1208 | 1209 | if [[ $REMOVE_UNBOUND == 'y' ]]; then 1210 | # Stop Unbound 1211 | systemctl stop unbound 1212 | 1213 | if [[ $OS =~ (debian|ubuntu) ]]; then 1214 | apt-get remove --purge -y unbound 1215 | elif [[ $OS == 'arch' ]]; then 1216 | pacman --noconfirm -R unbound 1217 | elif [[ $OS =~ (centos|amzn|oracle) ]]; then 1218 | yum remove -y unbound 1219 | elif [[ $OS == 'fedora' ]]; then 1220 | dnf remove -y unbound 1221 | fi 1222 | 1223 | rm -rf /etc/unbound/ 1224 | 1225 | echo "" 1226 | echo "Unbound removed!" 1227 | else 1228 | systemctl restart unbound 1229 | echo "" 1230 | echo "Unbound wasn't removed." 1231 | fi 1232 | } 1233 | 1234 | function removeOpenVPN() { 1235 | echo "" 1236 | read -rp "Do you really want to remove OpenVPN? [y/n]: " -e -i n REMOVE 1237 | if [[ $REMOVE == 'y' ]]; then 1238 | # Get OpenVPN port from the configuration 1239 | PORT=$(grep '^port ' /etc/openvpn/server.conf | cut -d " " -f 2) 1240 | PROTOCOL=$(grep '^proto ' /etc/openvpn/server.conf | cut -d " " -f 2) 1241 | 1242 | # Stop OpenVPN 1243 | if [[ $OS =~ (fedora|arch|centos|oracle) ]]; then 1244 | systemctl disable openvpn-server@server 1245 | systemctl stop openvpn-server@server 1246 | # Remove customised service 1247 | rm /etc/systemd/system/openvpn-server@.service 1248 | elif [[ $OS == "ubuntu" ]] && [[ $VERSION_ID == "16.04" ]]; then 1249 | systemctl disable openvpn 1250 | systemctl stop openvpn 1251 | else 1252 | systemctl disable openvpn@server 1253 | systemctl stop openvpn@server 1254 | # Remove customised service 1255 | rm /etc/systemd/system/openvpn\@.service 1256 | fi 1257 | 1258 | # Remove the iptables rules related to the script 1259 | systemctl stop iptables-openvpn 1260 | # Cleanup 1261 | systemctl disable iptables-openvpn 1262 | rm /etc/systemd/system/iptables-openvpn.service 1263 | systemctl daemon-reload 1264 | rm /etc/iptables/add-openvpn-rules.sh 1265 | rm /etc/iptables/rm-openvpn-rules.sh 1266 | 1267 | # SELinux 1268 | if hash sestatus 2>/dev/null; then 1269 | if sestatus | grep "Current mode" | grep -qs "enforcing"; then 1270 | if [[ $PORT != '1194' ]]; then 1271 | semanage port -d -t openvpn_port_t -p "$PROTOCOL" "$PORT" 1272 | fi 1273 | fi 1274 | fi 1275 | 1276 | if [[ $OS =~ (debian|ubuntu) ]]; then 1277 | apt-get remove --purge -y openvpn 1278 | if [[ -e /etc/apt/sources.list.d/openvpn.list ]]; then 1279 | rm /etc/apt/sources.list.d/openvpn.list 1280 | apt-get update 1281 | fi 1282 | elif [[ $OS == 'arch' ]]; then 1283 | pacman --noconfirm -R openvpn 1284 | elif [[ $OS =~ (centos|amzn|oracle) ]]; then 1285 | yum remove -y openvpn 1286 | elif [[ $OS == 'fedora' ]]; then 1287 | dnf remove -y openvpn 1288 | fi 1289 | 1290 | # Cleanup 1291 | find /home/ -maxdepth 2 -name "*.ovpn" -delete 1292 | find /root/ -maxdepth 1 -name "*.ovpn" -delete 1293 | rm -rf /etc/openvpn 1294 | rm -rf /usr/share/doc/openvpn* 1295 | rm -f /etc/sysctl.d/99-openvpn.conf 1296 | rm -rf /var/log/openvpn 1297 | 1298 | # Unbound 1299 | if [[ -e /etc/unbound/openvpn.conf ]]; then 1300 | removeUnbound 1301 | fi 1302 | echo "" 1303 | echo "OpenVPN removed!" 1304 | else 1305 | echo "" 1306 | echo "Removal aborted!" 1307 | fi 1308 | } 1309 | 1310 | function manageMenu() { 1311 | echo "Welcome to OpenVPN-install!" 1312 | echo "The git repository is available at: https://github.com/angristan/openvpn-install" 1313 | echo "" 1314 | echo "It looks like OpenVPN is already installed." 1315 | echo "" 1316 | echo "What do you want to do?" 1317 | echo " 1) Add a new user" 1318 | echo " 2) Revoke existing user" 1319 | echo " 3) Remove OpenVPN" 1320 | echo " 4) Exit" 1321 | until [[ $MENU_OPTION =~ ^[1-4]$ ]]; do 1322 | read -rp "Select an option [1-4]: " MENU_OPTION 1323 | done 1324 | 1325 | case $MENU_OPTION in 1326 | 1) 1327 | newClient 1328 | ;; 1329 | 2) 1330 | revokeClient 1331 | ;; 1332 | 3) 1333 | removeOpenVPN 1334 | ;; 1335 | 4) 1336 | exit 0 1337 | ;; 1338 | esac 1339 | } 1340 | 1341 | # Check for root, TUN, OS... 1342 | initialCheck 1343 | 1344 | # Check if OpenVPN is already installed 1345 | if [[ -e /etc/openvpn/server.conf && $AUTO_INSTALL != "y" ]]; then 1346 | manageMenu 1347 | else 1348 | installOpenVPN 1349 | fi --------------------------------------------------------------------------------