├── .codespellrc ├── .editorconfig ├── .github ├── CODEOWNERS ├── commit-message.template ├── pull_request_template.md └── workflows │ ├── pre-commit-check.yaml │ └── pre-commit-update.yaml ├── .gitignore ├── .gitlint ├── .pre-commit-config.yaml ├── LICENSE ├── README.md └── wireguard-mikrotik.sh /.codespellrc: -------------------------------------------------------------------------------- 1 | [codespell] 2 | builtin = clear,rare,names,code,en-GB_to_en-US 3 | check-filenames = 4 | context = 2 5 | enable-colors = true 6 | quiet-level = 3 7 | # TODO Add _false positives_ to the ignore list below 8 | # ignore-words-list = 9 | skip = .extras,.git,.password-store,.venv,build,Pipfile 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_size = 4 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | 9 | [*.yaml] 10 | indent_size = 2 11 | indent_style = space 12 | 13 | # kate: hl ini; 14 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Lines starting with '#' are comments. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # TODO Provide GitHub names here... 5 | # * @username1 @username2 ... 6 | -------------------------------------------------------------------------------- /.github/commit-message.template: -------------------------------------------------------------------------------- 1 | 2 | #--------------------------------------------------------------------- 3 | # Format for the commit title (the very first line) is the following: 4 | # 5 | # : (If applied, this commit will...) (max 72 chars) 6 | # 7 | # Where `` is one of the following: 8 | # - build = Changes to the build system 9 | # - ci = CI related changes (GH actions, hooks, ...) 10 | # - docs = Changes to the documentation/manuals 11 | # - feat = The commit introduces a new feature 12 | # - fix = The commit fixes a bug/regression/typo 13 | # - impr = The commit bring some improvements 14 | # - misc = Other actions 15 | # - package = Changes to the package produced 16 | # - refactor = The commit performs a refactoring 17 | # - release = The commit prepares a repo for release 18 | # - style = The commit fixes formatting 19 | # - testing = Changes to the tests or testing process 20 | # - wip = Work In Progress 21 | # 22 | # About conventional commits: https://www.conventionalcommits.org 23 | #--------------------------------------------------------------------- 24 | # NOTE: Leave the next line empty to separate title from the body 25 | 26 | # Explain why this change is being made below... (max 120 chars per line) 27 | 28 | 29 | # Optionally provide links or keys to any relevant tickets, articles or other resources. 30 | # See also: https://confluence.atlassian.com/fisheye/using-smart-commits-960155400.html 31 | 32 | #---[Git inserted text goes blow]------------------------------------- 33 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Changes in this PR 2 | 3 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /.github/workflows/pre-commit-check.yaml: -------------------------------------------------------------------------------- 1 | name: Checking `pre-commit` hooks 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | pull_request: 9 | branches: 10 | - master 11 | 12 | jobs: 13 | pr-checks: 14 | name: Checking `pre-commit` hooks 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v2 20 | 21 | - name: Check the `pre-commit` hook configuration presence 22 | id: check_files 23 | uses: andstor/file-existence-action@v1 24 | with: 25 | files: .pre-commit-config.yaml 26 | 27 | - name: Checking configured hooks 28 | if: steps.check_files.outputs.files_exists == 'true' 29 | uses: pre-commit/action@v2.0.3 30 | -------------------------------------------------------------------------------- /.github/workflows/pre-commit-update.yaml: -------------------------------------------------------------------------------- 1 | name: Pre-commit auto-update 2 | 3 | on: 4 | # every Sunday at midnight 5 | schedule: 6 | - cron: "0 0 * * 0" 7 | # on demand 8 | workflow_dispatch: 9 | 10 | jobs: 11 | auto-update: 12 | name: Checking `pre-commit` hooks updates 13 | 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout code 17 | uses: actions/checkout@v2 18 | 19 | - name: Prepare Python Environment 20 | uses: actions/setup-python@v2 21 | 22 | - name: Pre-installing requirements 23 | uses: browniebroke/pre-commit-autoupdate-action@main 24 | 25 | - name: Create PR to Update configs 26 | uses: peter-evans/create-pull-request@v3 27 | with: 28 | token: ${{ secrets.GITHUB_TOKEN }} 29 | branch: update/pre-commit-hooks 30 | title: 'misc: Version bump `pre-commit` hook plugins' 31 | commit-message: 'misc: Version bump `pre-commit` hook plugins' 32 | body: Update plugins of `pre-commit` hooks to latest version. 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /.gitlint: -------------------------------------------------------------------------------- 1 | # All these sections are optional, edit this file as you like. 2 | [general] 3 | # Ignore certain rules, you can reference them by their id or by their full name 4 | ignore=B6,T5 5 | 6 | # verbosity should be a value between 1 and 3, the commandline -v flags take precedence over this 7 | verbosity = 2 8 | 9 | # By default gitlint will ignore merge commits. Set to 'false' to disable. 10 | # ignore-merge-commits=true 11 | 12 | # By default gitlint will ignore fixup commits. Set to 'false' to disable. 13 | # ignore-fixup-commits=true 14 | 15 | # By default gitlint will ignore squash commits. Set to 'false' to disable. 16 | # ignore-squash-commits=true 17 | 18 | # Enable debug mode (prints more output). Disabled by default. 19 | # debug=true 20 | 21 | # Set the extra-path where gitlint will search for user defined rules 22 | # See http://jorisroovers.github.io/gitlint/user_defined_rules for details 23 | # extra-path=examples/ 24 | 25 | # NOTE Enable https://www.conventionalcommits.org/en/v1.0.0/ specification 26 | contrib=contrib-title-conventional-commits 27 | 28 | [title-max-length] 29 | line-length=72 30 | 31 | # [title-must-not-contain-word] 32 | # Comma-separated list of words that should not occur in the title. Matching is case 33 | # insensitive. It's fine if the keyword occurs as part of a larger word (so "WIPING" 34 | # will not cause a violation, but "WIP: my title" will. 35 | # words=wip 36 | 37 | # [title-match-regex] 38 | # python like regex (https://docs.python.org/2/library/re.html) that the 39 | # commit-msg title must be matched to. 40 | # Note that the regex can contradict with other rules if not used correctly 41 | # (e.g. title-must-not-contain-word). 42 | #regex=^(\[[A-Z0-9]+-[0-9]+\]|(Build|Documentation|Feature|Fix|Improvement|Misc|Package|Refactor|Release|Style|Testing|WIP):) .*$ 43 | 44 | [B1] 45 | # B1 = body-max-line-length 46 | line-length=120 47 | 48 | [body-min-length] 49 | min-length=0 50 | 51 | # [body-is-missing] 52 | # Whether to ignore this rule on merge commits (which typically only have a title) 53 | # default = False 54 | # gnore-merge-commits=false 55 | 56 | # [body-changed-file-mention] 57 | # List of files that need to be explicitly mentioned in the body when they are changed 58 | # This is useful for when developers often erroneously edit certain files or git submodules. 59 | # By specifying this rule, developers can only change the file when they explicitly reference 60 | # it in the commit message. 61 | # files=gitlint/rules.py,README.md 62 | 63 | # [author-valid-email] 64 | # python like regex (https://docs.python.org/2/library/re.html) that the 65 | # commit author email address should be matched to 66 | # For example, use the following regex if you only want to allow email addresses from foo.com 67 | # regex=[^@]+@foo.com 68 | 69 | # [ignore-by-title] 70 | # Ignore certain rules for commits of which the title matches a regex 71 | # E.g. Match commit titles that start with "Release" 72 | # regex=^Release(.*) 73 | # 74 | # Ignore certain rules, you can reference them by their id or by their full name 75 | # Use 'all' to ignore all rules 76 | # ignore=T1,body-min-length 77 | 78 | # [ignore-by-body] 79 | # Ignore certain rules for commits of which the body has a line that matches a regex 80 | # E.g. Match bodies that have a line that that contain "release" 81 | # regex=(.*)release(.*) 82 | # 83 | # Ignore certain rules, you can reference them by their id or by their full name 84 | # Use 'all' to ignore all rules 85 | # ignore=T1,body-min-length 86 | 87 | [contrib-title-conventional-commits] 88 | types=build,ci,docs,feat,fix,impr,misc,refactor,release,style,testing,wip 89 | 90 | # kate: hl ini; 91 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # Read the docs here: https://pre-commit.com and http://jorisroovers.github.io/gitlint/ 2 | # Install hooks to your clone: 3 | # $ pre-commit install --install-hooks 4 | # and 5 | # $ pre-commit install --install-hooks -t commit-msg 6 | 7 | default_stages: [pre-commit] 8 | fail_fast: false 9 | repos: 10 | - repo: meta 11 | hooks: 12 | - id: check-hooks-apply 13 | - id: check-useless-excludes 14 | 15 | - repo: https://github.com/pre-commit/pre-commit-hooks 16 | rev: v5.0.0 17 | hooks: 18 | - id: check-case-conflict 19 | - id: check-yaml 20 | - id: end-of-file-fixer 21 | - id: trailing-whitespace 22 | 23 | - repo: https://github.com/jorisroovers/gitlint 24 | rev: v0.19.1 25 | hooks: 26 | - id: gitlint 27 | name: Commit message check 28 | language: python 29 | entry: gitlint --msg-filename 30 | stages: [commit-msg] 31 | 32 | - repo: https://github.com/codespell-project/codespell 33 | rev: v2.4.1 34 | hooks: 35 | - id: codespell 36 | 37 | - repo: https://github.com/koalaman/shellcheck-precommit 38 | rev: v0.10.0 39 | hooks: 40 | - id: shellcheck 41 | args: ['--shell=bash', '--color=always'] 42 | files: '\.sh$' 43 | types: ['file', 'text'] 44 | 45 | # kate: indent-width 2; tab-width 2; 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 IgorKha 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WireGuard MikroTik config generator 2 | 3 | ![visitors](https://visitor-badge.laobi.icu/badge?page_id=IgorKha.wireguard-mikrotik) 4 | 5 | **This project is a bash script designed to simplify the configuration of a [WireGuard](https://www.wireguard.com/) VPN on a MikroTik device.** 6 | 7 | WireGuard is a point-to-point VPN protocol that offers various usage possibilities. In this context, we refer to a VPN where the client's traffic is securely tunneled to the server. The server applies NAT to the client's traffic, making it appear as though the client is accessing the internet using the server's IP address. 8 | 9 | The script fully supports both IPv4 and IPv6. Please refer to the [issues](https://github.com/IgorKha/wireguard-mikrotik/issues) section for ongoing development, bug reports, and planned features. 10 | 11 | A portion of this script is based on the following repository: [wireguard-install](https://github.com/angristan/wireguard-install) 12 | 13 | For monitoring the performance of your MikroTik device, you can use [Grafana-MikroTik](https://github.com/IgorKha/Grafana-Mikrotik) to visualize measurements. 14 | 15 | ## Requirements 16 | 17 | Packages: 18 | 19 | - wireguard-tools 20 | - qrencode 21 | 22 | Supported distributions: 23 | 24 | - Ubuntu >= 16.04 25 | - Debian >= 10 26 | - Fedora 27 | - CentOS 28 | - Arch Linux 29 | - Oracle Linux 30 | - macOS (experimental) [**requires** [**brew**](https://brew.sh) package manager] 31 | 32 | ## Usage 33 | 34 | Download and execute the script on superuser. Answer the questions asked by the script and it will take care of the rest. 35 | 36 | ```bash 37 | curl -O https://raw.githubusercontent.com/IgorKha/wireguard-mikrotik/master/wireguard-mikrotik.sh 38 | chmod +x wireguard-mikrotik.sh 39 | ./wireguard-mikrotik.sh 40 | ``` 41 | 42 | Once run, the script will create a "wireguard" folder with your settings. 43 | 44 | Run the script again to add clients (enter exist interface name) or create new server (enter new interface name)! 45 | 46 | [![asciicast](https://asciinema.org/a/64wco1fA8k251anGsSQDcH9jW.svg)](https://asciinema.org/a/64wco1fA8k251anGsSQDcH9jW) 47 | 48 | ## Structure 49 | 50 | ```text 51 | . 52 | ├── wireguard 53 | │   ├── wg0 - WireGuard interface name (server name) 54 | │   │   ├── client - clients config folder 55 | │   │   │   └── user1 56 | │   │   │   ├── mikrotik-peer-wg0-client-user1.rsc - MikroTik peer config [server side] 57 | │   │   │   ├── wg0-client-user1.conf - config file for your client 58 | │   │   │   └── wg0-client-user1.png - and QR client config 59 | │   │   ├── mikrotik 60 | │   │   │   └── wg0.rsc - paste in your mikrotik console 61 | │   │   ├── params 62 | │   │   └── wg0.conf 63 | │   └── wg1 - WireGuard interface name (server name) 64 | │   ├── client - clients config folder 65 | │   │   ├── user1 66 | │   │   │   ├── mikrotik-peer-wg1-client-user1.rsc 67 | │   │   │   ├── wg1-client-user1.conf 68 | │   │   │   └── wg1-client-user1.png 69 | │   │   └── user2 70 | │   │   ├── mikrotik-peer-wg1-client-user2.rsc 71 | │   │   ├── wg1-client-user2.conf 72 | │   │   └── wg1-client-user2.png 73 | │   ├── mikrotik 74 | │   │   └── wg1.rsc 75 | │   ├── params 76 | │   └── wg1.conf 77 | └── wireguard-mikrotik.sh 78 | ``` 79 | -------------------------------------------------------------------------------- /wireguard-mikrotik.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | BLUE='\033[0;34m' 4 | NC='\033[0m' 5 | INFO="${BLUE}[i]${NC}" 6 | 7 | function checkOS() { 8 | 9 | #? Check OS version 10 | if [[ -e /etc/debian_version ]]; then 11 | # shellcheck source=/dev/null 12 | source /etc/os-release 13 | OS="${ID}" # debian or ubuntu 14 | if [[ ${ID} == "debian" || ${ID} == "raspbian" ]]; then 15 | if [[ ${VERSION_ID} -lt 10 ]]; then 16 | echo "Your version of Debian (${VERSION_ID}) is not supported. Please use Debian 10 Buster or later" 17 | exit 95 18 | fi 19 | OS=debian #* overwrite if raspbian 20 | fi 21 | elif [[ -e /etc/fedora-release ]]; then 22 | # shellcheck source=/dev/null 23 | source /etc/os-release 24 | OS="${ID}" 25 | elif [[ -e /etc/centos-release ]]; then 26 | # shellcheck source=/dev/null 27 | source /etc/os-release 28 | OS=centos 29 | elif [[ -e /etc/oracle-release ]]; then 30 | # shellcheck source=/dev/null 31 | source /etc/os-release 32 | OS=oracle 33 | elif [[ -e /etc/arch-release ]]; then 34 | OS=arch 35 | elif [[ "$(uname -s)" == "Darwin" ]]; then 36 | OS=macos 37 | else 38 | echo "Looks like you aren't running this installer on a Debian, Ubuntu, Fedora, CentOS, Oracle or Arch Linux system" 39 | exit 95 40 | fi 41 | export OS 42 | } 43 | 44 | function installWireGuard() { 45 | 46 | #? Check root user 47 | if [[ "${EUID}" -ne 0 ]] && [[ "${OS}" != "macos" ]]; then 48 | echo "" 49 | echo "You need to run this script as root" 50 | echo "" 51 | exit 13 52 | fi 53 | 54 | #? Install WireGuard tools and module 55 | if [[ ${OS} == 'ubuntu' ]] || [[ ${OS} == 'debian' && ${VERSION_ID} -gt 10 ]]; then 56 | apt-get update 57 | apt-get install -y wireguard qrencode 58 | elif [[ ${OS} == 'debian' ]]; then 59 | if ! grep -rqs "^deb .* buster-backports" /etc/apt/; then 60 | echo "deb http://deb.debian.org/debian buster-backports main" >/etc/apt/sources.list.d/backports.list 61 | apt-get update 62 | fi 63 | apt update 64 | apt-get install -y qrencode 65 | apt-get install -y -t buster-backports wireguard 66 | elif [[ ${OS} == 'fedora' ]]; then 67 | if [[ ${VERSION_ID} -lt 32 ]]; then 68 | dnf install -y dnf-plugins-core 69 | dnf copr enable -y jdoss/wireguard 70 | dnf install -y wireguard-dkms 71 | fi 72 | dnf install -y wireguard-tools qrencode 73 | elif [[ ${OS} == 'centos' ]]; then 74 | yum -y install epel-release elrepo-release 75 | if [[ ${VERSION_ID} -eq 7 ]]; then 76 | yum -y install yum-plugin-elrepo 77 | fi 78 | yum -y install kmod-wireguard wireguard-tools qrencode 79 | elif [[ ${OS} == 'oracle' ]]; then 80 | dnf install -y oraclelinux-developer-release-el8 81 | dnf config-manager --disable -y ol8_developer 82 | dnf config-manager --enable -y ol8_developer_UEKR6 83 | dnf config-manager --save -y --setopt=ol8_developer_UEKR6.includepkgs='wireguard-tools*' 84 | dnf install -y wireguard-tools qrencode 85 | elif [[ ${OS} == 'arch' ]]; then 86 | pacman -Sq --needed --noconfirm wireguard-tools qrencode 87 | elif [[ ${OS} == 'macos' ]]; then 88 | if ! command -v brew &> /dev/null 89 | then 90 | echo "" 91 | echo "Brew is not installed. Please install it and run this script again." 92 | echo "https://brew.sh/" 93 | exit 1 94 | fi 95 | brew install wireguard-tools qrencode 96 | fi 97 | echo "" 98 | echo "The installation is complete. Now you need to re-run the script with user access rights (not root)." 99 | echo "" 100 | exit 0 101 | } 102 | 103 | function installCheck() { 104 | if ! command -v wg &> /dev/null 105 | then 106 | echo "You must have \"wireguard-tools\" and \"qrencode\" installed." 107 | read -n1 -r -p "Press any key to continue and install needed packages..." 108 | installWireGuard 109 | fi 110 | } 111 | 112 | function serverName() { 113 | until [[ ${SERVER_WG_NIC} =~ ^[a-zA-Z0-9_]+$ && ${#SERVER_WG_NIC} -lt 16 ]]; do 114 | echo "Tell me a name for the server WireGuard interface. ('wg0' is used by default)" 115 | read -rp "WireGuard interface name (server name): " -e SERVER_WG_NIC 116 | SERVER_WG_NIC=${SERVER_WG_NIC:-wg0} 117 | done 118 | } 119 | 120 | function installQuestions() { 121 | echo "I need to ask you a few questions before starting the setup." 122 | echo "You can leave the default options and just press enter if you are ok with them." 123 | echo "" 124 | 125 | # Detect public IPv4 or IPv6 address and pre-fill for the user 126 | SERVER_PUB_IP=$(host myip.opendns.com resolver1.opendns.com | grep -oE 'has address [0-9.]+' | cut -d ' ' -f3) 127 | echo "Your public IPv4 address is ${SERVER_PUB_IP}" 128 | if [[ -z ${SERVER_PUB_IP} ]]; then 129 | # Detect public IPv6 address 130 | if [[ ${OS} == "macos" ]]; then 131 | # Detect public IPv6 address on macOS 132 | SERVER_PUB_IP=$(ifconfig | grep -A4 'en0:' | grep 'inet6' | awk '{print $2}') 133 | else 134 | # Detect public IPv6 address on Linux 135 | SERVER_PUB_IP=$(ip -6 addr | sed -ne 's|^.* inet6 \([^/]*\)/.* scope global.*$|\1|p' | head -1) 136 | fi 137 | fi 138 | 139 | # while true; do 140 | # read -rp "Enter IPv4 or IPv6 public address: " -e -i "${SERVER_PUB_IP}" SERVER_PUB_IP 141 | while true; do 142 | read -rp "Enter IPv4 or IPv6 public address [default used ${SERVER_PUB_IP}]: " -e USER_INPUT_SERVER_PUB_IP 143 | SERVER_PUB_IP=${USER_INPUT_SERVER_PUB_IP:-$SERVER_PUB_IP} 144 | if [[ ${SERVER_PUB_IP} =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then 145 | break 146 | elif [[ ${SERVER_PUB_IP} =~ ^[0-9a-fA-F:]+:[0-9a-fA-F:]*$ ]]; then 147 | SERVER_PUB_IP="[${SERVER_PUB_IP}]" 148 | break 149 | else 150 | echo "Invalid IP address. Please enter a valid IPv4 or IPv6 address." 151 | fi 152 | done 153 | 154 | until [[ ${SERVER_WG_IPV4} =~ ^([0-9]{1,3}\.){3} ]]; do 155 | # read -rp "Server's WireGuard IPv4: " -e -i 10."$(shuf -i 0-250 -n 1)"."$(shuf -i 0-250 -n 1)".1 SERVER_WG_IPV4 156 | if [[ ${OS} == "macos" ]]; then 157 | SERVER_WG_IPV4="10.$(jot -r 1 0 250).$(jot -r 1 0 250).1" 158 | read -rp "Server's WireGuard IPv4 [default used ${SERVER_WG_IPV4}]: " -e USER_INPUT_SERVER_WG_IPV4 159 | SERVER_WG_IPV4=${USER_INPUT_SERVER_WG_IPV4:-$SERVER_WG_IPV4} 160 | else 161 | read -rp "Server's WireGuard IPv4: " -e -i 10."$(shuf -i 0-250 -n 1)"."$(shuf -i 0-250 -n 1)".1 SERVER_WG_IPV4 162 | fi 163 | done 164 | 165 | until [[ ${SERVER_WG_IPV6} =~ ^([a-f0-9]{1,4}:){3,4}: ]]; do 166 | # read -rp "Server's WireGuard IPv6: " -e -i fd42:"$(shuf -i 10-90 -n 1)":"$(shuf -i 10-90 -n 1)"::1 SERVER_WG_IPV6 167 | if [[ ${OS} == 'macos' ]]; then 168 | SERVER_WG_IPV6="fd42:$(jot -r 1 10 90):$(jot -r 1 10 90)::1" 169 | read -rp "Server's WireGuard IPv6 [default used ${SERVER_WG_IPV6}]: " -e USER_INPUT_SERVER_WG_IPV6 170 | SERVER_WG_IPV6=${USER_INPUT_SERVER_WG_IPV6:-$SERVER_WG_IPV6} 171 | else 172 | read -rp "Server's WireGuard IPv6: " -e -i fd42:"$(shuf -i 10-90 -n 1)":"$(shuf -i 10-90 -n 1)"::1 SERVER_WG_IPV6 173 | fi 174 | done 175 | 176 | # Generate random number within private ports range 177 | RANDOM_PORT=$(shuf -i 49152-65535 -n1) 178 | until [[ ${SERVER_PORT} =~ ^[0-9]+$ ]] && [ "${SERVER_PORT}" -ge 1 ] && [ "${SERVER_PORT}" -le 65535 ]; do 179 | # read -rp "Server's WireGuard port [1-65535]: " -e -i "${RANDOM_PORT}" SERVER_PORT 180 | if [[ ${OS} == 'macos' ]]; then 181 | read -rp "Server's WireGuard port [1-65535] [default ${RANDOM_PORT}]: " -e USER_INPUT_SERVER_PORT 182 | SERVER_PORT=${USER_INPUT_SERVER_PORT:-$RANDOM_PORT} 183 | else 184 | read -rp "Server's WireGuard port [1-65535]: " -e -i "${RANDOM_PORT}" SERVER_PORT 185 | fi 186 | done 187 | 188 | # Adguard DNS by default 189 | until [[ ${CLIENT_DNS_1} =~ ^((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 190 | # read -rp "First DNS resolver to use for the clients: " -e -i 94.140.14.14 CLIENT_DNS_1 191 | if [[ ${OS} == 'macos' ]]; then 192 | CLIENT_DNS_1='94.140.14.14' 193 | read -rp "First DNS resolver to use for the clients [default ${CLIENT_DNS_1}]: " -e USER_INPUT_CLIENT_DNS_1 194 | CLIENT_DNS_1=${USER_INPUT_CLIENT_DNS_1:-$CLIENT_DNS_1} 195 | else 196 | read -rp "First DNS resolver to use for the clients: " -e -i 94.140.14.14 CLIENT_DNS_1 197 | fi 198 | done 199 | until [[ ${CLIENT_DNS_2} =~ ^((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 200 | if [[ ${OS} == 'macos' ]]; then 201 | CLIENT_DNS_DEF_2='94.140.15.15' 202 | read -rp "Second DNS resolver to use for the clients (optional) [default ${CLIENT_DNS_DEF_2}]: " -e USER_INPUT_CLIENT_DNS_2 203 | CLIENT_DNS_2=${USER_INPUT_CLIENT_DNS_2:-$CLIENT_DNS_DEF_2} 204 | else 205 | read -rp "Second DNS resolver to use for the clients (optional): " -e -i 94.140.15.15 CLIENT_DNS_2 206 | if [[ ${CLIENT_DNS_2} == "" ]]; then 207 | CLIENT_DNS_2="${CLIENT_DNS_1}" 208 | fi 209 | fi 210 | done 211 | 212 | echo "" 213 | echo "Okay, that was all I needed. We are ready to setup your WireGuard server now." 214 | echo "You will be able to generate a client at the end of the installation." 215 | read -n1 -r -p "Press any key to continue..." 216 | } 217 | 218 | function newInterface() { 219 | # Run setup questions first 220 | installQuestions 221 | 222 | # Make sure the directory exists (this does not seem the be the case on fedora) 223 | mkdir -p "$(pwd)"/wireguard/"${SERVER_WG_NIC}"/mikrotik >/dev/null 2>&1 224 | 225 | SERVER_PRIV_KEY=$(wg genkey) 226 | SERVER_PUB_KEY=$(echo "${SERVER_PRIV_KEY}" | wg pubkey) 227 | 228 | # Save WireGuard settings #SERVER_PUB_NIC=${SERVER_PUB_NIC} 229 | echo "SERVER_PUB_IP=${SERVER_PUB_IP} 230 | 231 | SERVER_WG_NIC=${SERVER_WG_NIC} 232 | SERVER_WG_IPV4=${SERVER_WG_IPV4} 233 | SERVER_WG_IPV6=${SERVER_WG_IPV6} 234 | SERVER_PORT=${SERVER_PORT} 235 | SERVER_PRIV_KEY=${SERVER_PRIV_KEY} 236 | SERVER_PUB_KEY=${SERVER_PUB_KEY} 237 | CLIENT_DNS_1=${CLIENT_DNS_1} 238 | CLIENT_DNS_2=${CLIENT_DNS_2}" > "$(pwd)/wireguard/${SERVER_WG_NIC}/params" 239 | 240 | # Save WireGuard settings to the MikroTik 241 | echo "# WireGuard interface configure 242 | /interface wireguard 243 | add listen-port=${SERVER_PORT} mtu=1420 name=${SERVER_WG_NIC} private-key=\\ 244 | \"${SERVER_PRIV_KEY}\" 245 | /ip firewall filter 246 | add action=accept chain=input comment=wg-${SERVER_WG_NIC} dst-port=${SERVER_PORT} protocol=udp 247 | /ip firewall filter move [/ip firewall filter find comment=wg-${SERVER_WG_NIC}] 1 248 | /ip address 249 | add address=${SERVER_WG_IPV4}/24 comment=wg-${SERVER_WG_NIC} interface=${SERVER_WG_NIC} 250 | " > "$(pwd)/wireguard/${SERVER_WG_NIC}/mikrotik/${SERVER_WG_NIC}.rsc" 251 | 252 | 253 | # Add server interface 254 | echo "[Interface] 255 | Address = ${SERVER_WG_IPV4}/24,${SERVER_WG_IPV6}/64 256 | ListenPort = ${SERVER_PORT} 257 | PrivateKey = ${SERVER_PRIV_KEY}" > "$(pwd)/wireguard/${SERVER_WG_NIC}/${SERVER_WG_NIC}.conf" 258 | 259 | newClient 260 | echo -e "${INFO} MikroTik interface config available in $(pwd)/wireguard/${SERVER_WG_NIC}/mikrotik/${SERVER_WG_NIC}.rsc" 261 | echo -e "${INFO} If you want to add more clients, you simply need to run this script another time!" 262 | 263 | } 264 | 265 | function newClient() { 266 | ENDPOINT="${SERVER_PUB_IP}:${SERVER_PORT}" 267 | 268 | echo "" 269 | echo "Tell me a name for the client." 270 | echo "The name must consist of alphanumeric character. It may also include an underscore or a dash and can't exceed 15 chars." 271 | 272 | until [[ ${CLIENT_NAME} =~ ^[a-zA-Z0-9_-]+$ && ${CLIENT_EXISTS} == '0' && ${#CLIENT_NAME} -lt 16 ]]; do 273 | read -rp "Client name: " -e CLIENT_NAME 274 | CLIENT_EXISTS=$(grep -c -E "^### Client ${CLIENT_NAME}\$" "$(pwd)/wireguard/${SERVER_WG_NIC}/${SERVER_WG_NIC}.conf") 275 | 276 | if [[ ${CLIENT_EXISTS} == '1' ]]; then 277 | echo "" 278 | echo "A client with the specified name was already created, please choose another name." 279 | echo "" 280 | fi 281 | done 282 | 283 | for DOT_IP in {2..254}; do 284 | if [[ ${OS} == 'macos' ]]; then 285 | DOT_EXISTS=$(grep -c "$(echo "${SERVER_WG_IPV4}" | rev | cut -c 2- | rev)${DOT_IP}" "$(pwd)/wireguard/${SERVER_WG_NIC}/${SERVER_WG_NIC}.conf") 286 | else 287 | DOT_EXISTS=$(grep -c "${SERVER_WG_IPV4::-1}${DOT_IP}" "$(pwd)/wireguard/${SERVER_WG_NIC}/${SERVER_WG_NIC}.conf") 288 | fi 289 | if [[ ${DOT_EXISTS} == '0' ]]; then 290 | break 291 | fi 292 | done 293 | 294 | if [[ ${DOT_EXISTS} == '1' ]]; then 295 | echo "" 296 | echo "The subnet configured supports only 253 clients." 297 | exit 99 298 | fi 299 | 300 | BASE_IP=$(echo "$SERVER_WG_IPV4" | awk -F '.' '{ print $1"."$2"."$3 }') 301 | until [[ ${IPV4_EXISTS} == '0' ]]; do 302 | if [[ ${OS} == 'macos' ]]; then 303 | read -rp "Client's WireGuard IPv4 [default used ${BASE_IP}.${DOT_IP}]: " -e USER_INPUT_DOT_IP 304 | DOT_IP=${USER_INPUT_DOT_IP:-$DOT_IP} 305 | else 306 | read -rp "Client's WireGuard IPv4: ${BASE_IP}." -e -i "${DOT_IP}" DOT_IP 307 | fi 308 | CLIENT_WG_IPV4="${BASE_IP}.${DOT_IP}" 309 | IPV4_EXISTS=$(grep -c "$CLIENT_WG_IPV4/24" "$(pwd)/wireguard/${SERVER_WG_NIC}/${SERVER_WG_NIC}.conf") 310 | 311 | if [[ ${IPV4_EXISTS} == '1' ]]; then 312 | echo "" 313 | echo "A client with the specified IPv4 was already created, please choose another IPv4." 314 | echo "" 315 | fi 316 | done 317 | 318 | BASE_IP=$(echo "$SERVER_WG_IPV6" | awk -F '::' '{ print $1 }') 319 | until [[ ${IPV6_EXISTS} == '0' ]]; do 320 | if [[ ${OS} == 'macos' ]]; then 321 | read -rp "Client's WireGuard IPv6 [default used ${BASE_IP}::${DOT_IP}]: " -e USER_INPUT_DOT_IP 322 | DOT_IP=${USER_INPUT_DOT_IP:-$DOT_IP} 323 | else 324 | read -rp "Client's WireGuard IPv6: ${BASE_IP}::" -e -i "${DOT_IP}" DOT_IP 325 | fi 326 | CLIENT_WG_IPV6="${BASE_IP}::${DOT_IP}" 327 | IPV6_EXISTS=$(grep -c "${CLIENT_WG_IPV6}/64" "$(pwd)/wireguard/${SERVER_WG_NIC}/${SERVER_WG_NIC}.conf") 328 | 329 | if [[ ${IPV6_EXISTS} == '1' ]]; then 330 | echo "" 331 | echo "A client with the specified IPv6 was already created, please choose another IPv6." 332 | echo "" 333 | fi 334 | done 335 | 336 | # Asking for client's allowed IPs 337 | until [[ ${ALLOWED_IPV4} =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}(\/([0-9]|[1-2][0-9]|3[0-2]))?$ ]]; do 338 | if [[ ${OS} == 'macos' ]]; then 339 | ALLOWED_IPV4="0.0.0.0/0" 340 | read -rp "Client's allowed IPv4 [default used ${ALLOWED_IPV4}]: " -e USER_INPUT_ALLOWED_IPV4 341 | ALLOWED_IPV4=${USER_INPUT_ALLOWED_IPV4:-$ALLOWED_IPV4} 342 | else 343 | read -rp "Client's allowed IPv4: " -e -i "0.0.0.0/0" ALLOWED_IPV4 344 | fi 345 | done 346 | 347 | until [[ ${ALLOWED_IPV6} =~ ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(\/((1(1[0-9]|2[0-8]))|([0-9][0-9])|([0-9])))?$ ]]; do 348 | if [[ ${OS} == 'macos' ]]; then 349 | ALLOWED_IPV6="::/0" 350 | read -rp "Client's allowed IPv6 [default used ${ALLOWED_IPV6}]: " -e USER_INPUT_ALLOWED_IPV6 351 | ALLOWED_IPV6=${USER_INPUT_ALLOWED_IPV6:-$ALLOWED_IPV6} 352 | else 353 | read -rp "Client's allowed IPv6: " -e -i "::/0" ALLOWED_IPV6 354 | fi 355 | done 356 | 357 | # Generate key pair for the client 358 | CLIENT_PRIV_KEY=$(wg genkey) 359 | CLIENT_PUB_KEY=$(echo "${CLIENT_PRIV_KEY}" | wg pubkey) 360 | CLIENT_PRE_SHARED_KEY=$(wg genpsk) 361 | 362 | mkdir -p "$(pwd)/wireguard/${SERVER_WG_NIC}/client/${CLIENT_NAME}" >/dev/null 2>&1 363 | HOME_DIR="$(pwd)/wireguard/${SERVER_WG_NIC}/client/${CLIENT_NAME}" 364 | 365 | # Create client file and add the server as a peer 366 | echo "[Interface] 367 | PrivateKey = ${CLIENT_PRIV_KEY} 368 | Address = ${CLIENT_WG_IPV4}/32,${CLIENT_WG_IPV6}/128 369 | DNS = ${CLIENT_DNS_1},${CLIENT_DNS_2} 370 | 371 | [Peer] 372 | PublicKey = ${SERVER_PUB_KEY} 373 | PresharedKey = ${CLIENT_PRE_SHARED_KEY} 374 | Endpoint = ${ENDPOINT} 375 | AllowedIPs = ${ALLOWED_IPV4},${ALLOWED_IPV6}" >>"${HOME_DIR}/${SERVER_WG_NIC}-client-${CLIENT_NAME}.conf" 376 | 377 | # Add the client as a peer to the MikroTik (to client folder) 378 | echo "# WireGuard client peer configure 379 | /interface wireguard peers 380 | add allowed-address=${CLIENT_WG_IPV4}/32 comment=\\ 381 | ${SERVER_WG_NIC}-client-${CLIENT_NAME} interface=${SERVER_WG_NIC} \\ 382 | preshared-key=\"${CLIENT_PRE_SHARED_KEY}\" public-key=\\ 383 | \"${CLIENT_PUB_KEY}\" 384 | " >"${HOME_DIR}/mikrotik-peer-${SERVER_WG_NIC}-client-${CLIENT_NAME}.rsc" 385 | 386 | # Add the client as a peer to the MikroTik 387 | echo "# WireGuard client peer configure 388 | /interface wireguard peers 389 | add allowed-address=${CLIENT_WG_IPV4}/32 comment=\\ 390 | ${SERVER_WG_NIC}-client-${CLIENT_NAME} interface=${SERVER_WG_NIC} \\ 391 | preshared-key=\"${CLIENT_PRE_SHARED_KEY}\" public-key=\\ 392 | \"${CLIENT_PUB_KEY}\" 393 | " >> "$(pwd)/wireguard/${SERVER_WG_NIC}/mikrotik/${SERVER_WG_NIC}.rsc" 394 | 395 | # Add the client as a peer to the server 396 | echo -e "\n### Client ${CLIENT_NAME} 397 | [Peer] 398 | PublicKey = ${CLIENT_PUB_KEY} 399 | PresharedKey = ${CLIENT_PRE_SHARED_KEY} 400 | AllowedIPs = ${CLIENT_WG_IPV4}/32,${CLIENT_WG_IPV6}/128" >>"$(pwd)/wireguard/${SERVER_WG_NIC}/${SERVER_WG_NIC}.conf" 401 | 402 | echo -e "\nHere is your client config file as a QR Code:" 403 | 404 | qrencode -t ansiutf8 -l L <"${HOME_DIR}/${SERVER_WG_NIC}-client-${CLIENT_NAME}.conf" 405 | qrencode -l L -s 6 -d 225 -o "${HOME_DIR}/${SERVER_WG_NIC}-client-${CLIENT_NAME}.png" <"${HOME_DIR}/${SERVER_WG_NIC}-client-${CLIENT_NAME}.conf" 406 | 407 | echo -e "${INFO} Config available in ${HOME_DIR}/${SERVER_WG_NIC}-client-${CLIENT_NAME}.conf" 408 | echo -e "${INFO} QR is also available in ${HOME_DIR}/${SERVER_WG_NIC}-client-${CLIENT_NAME}.png" 409 | echo -e "${INFO} MikroTik peer config available in ${HOME_DIR}/mikrotik-${SERVER_WG_NIC}-client-${CLIENT_NAME}.rsc" 410 | } 411 | 412 | function manageMenu() { 413 | echo "" 414 | echo "It looks like this WireGuard interface is already." 415 | echo "" 416 | echo "What do you want to do?" 417 | echo " 1) Add a new client" 418 | echo " 2) Exit" 419 | until [[ ${MENU_OPTION} =~ ^[1-4]$ ]]; do 420 | read -rp "Select an option [1-2]: " MENU_OPTION 421 | done 422 | case "${MENU_OPTION}" in 423 | 1) 424 | newClient 425 | ;; 426 | 2) 427 | exit 0 428 | ;; 429 | esac 430 | } 431 | 432 | #? List of existing configurations 433 | function listConfs() { 434 | local directory 435 | directory="$(pwd)/wireguard" 436 | 437 | if [ -d "${directory}" ]; then 438 | echo "List of existing configurations:" 439 | i=1 440 | for folder in "${directory}"/*/; do 441 | local users count folder_name 442 | users="${folder}/client/" 443 | count=$(find "$users" -maxdepth 1 -mindepth 1 -type d 2>/dev/null | wc -l) 444 | folder_name=$(basename "${folder}") 445 | echo "${i}. ${folder_name} [${count} user(s)]" 446 | ((i++)) 447 | done 448 | fi 449 | echo "" 450 | } 451 | 452 | echo "" 453 | echo "Welcome to WireGuard-MikroTik configurator!" 454 | echo "The git repository is available at: https://github.com/IgorKha/wireguard-mikrotik" 455 | echo "" 456 | 457 | #? Check OS 458 | checkOS 459 | echo "Your OS is ${OS}" 460 | 461 | #? Check for root, WireGuard 462 | installCheck 463 | 464 | listConfs 465 | 466 | #? Check server exist 467 | serverName 468 | 469 | #? Check if WireGuard is already installed and load params 470 | if [[ -e $(pwd)/wireguard/${SERVER_WG_NIC}/params ]]; then 471 | # shellcheck source=/dev/null 472 | source "$(pwd)/wireguard/${SERVER_WG_NIC}/params" 473 | manageMenu 474 | else 475 | newInterface 476 | fi 477 | --------------------------------------------------------------------------------