├── .gitignore ├── openvpn-server ├── fw-rules.sh ├── checkpsw.sh ├── db │ └── .gitignore ├── clients │ └── .gitignore ├── pki │ └── .gitignore ├── staticclients │ └── .gitignore ├── openvpn-docker │ ├── bin │ │ ├── oath-sec-gen.sh │ │ ├── oath.sh │ │ ├── rmcert.sh │ │ ├── revoke.sh │ │ └── genclient.sh │ ├── Dockerfile │ ├── openssl-easyrsa.cnf │ └── README.md ├── server.conf ├── config │ └── old-server.conf └── backup.sh ├── images ├── OVPN_VLANs.png ├── OVPN_Dashboard.png ├── OVPN_New_Client.png ├── OVPN_Palm_import.png ├── OpenVPN-UI-Certs.png ├── OpenVPN-UI-Home.png ├── OpenVPN-UI-Login.png ├── OpenVPN-UI-Logs.png ├── OVPN_ext_serv_ip1.png ├── OVPN_ext_serv_ip2.png ├── OpenVPN-UI-Config.png ├── OpenVPN-UI-Profile.png ├── OpenVPN-UI-Restart.png ├── OpenVPN-UI-Revoke.png ├── OVPN_Palm_connected.png ├── OpenVPN-EC2-OVPN-Only.png ├── OpenVPN-EC2-UI-Only.png ├── OVPN_New_Client_download.png └── OpenVPN-UI-Server-config.png ├── ansible.cfg ├── requirements.yml ├── templates ├── grafana-config.monitoring.j2 ├── prometheus.yml.j2 ├── openvpn_client.conf.j2 ├── easy-rsa.vars.j2 ├── openvpn_exporter-docker-compose.yml.j2 ├── openvpn-docker-compose.yml.j2 ├── openvpn-docker-entrypoint.sh.j2 └── openvpn-dashboard.json.j2 ├── monitoring ├── grafana │ └── provisioning │ │ ├── dashboards │ │ └── dashboard.yml │ │ └── datasources │ │ └── datasource.yml └── prometheus │ └── alert.rules ├── tasks ├── handlers.yml ├── handlers_ubuntu.yml ├── debian-libseccomp-update.yml ├── docker_ubuntu.yml ├── openvpn_exporter.yml ├── openvpn.yml ├── openvpn_ubuntu.yml ├── docker.yml ├── openvpn_monitoring.yml └── openvpn_monitoring_ubuntu.yml ├── inventory.yml ├── main.yml ├── config.yml ├── example.config.yml ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /openvpn-server/fw-rules.sh: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /openvpn-server/checkpsw.sh: -------------------------------------------------------------------------------- 1 | # this is your default check password script -------------------------------------------------------------------------------- /images/OVPN_VLANs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d3vilh/openvpn-aws/HEAD/images/OVPN_VLANs.png -------------------------------------------------------------------------------- /images/OVPN_Dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d3vilh/openvpn-aws/HEAD/images/OVPN_Dashboard.png -------------------------------------------------------------------------------- /images/OVPN_New_Client.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d3vilh/openvpn-aws/HEAD/images/OVPN_New_Client.png -------------------------------------------------------------------------------- /images/OVPN_Palm_import.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d3vilh/openvpn-aws/HEAD/images/OVPN_Palm_import.png -------------------------------------------------------------------------------- /images/OpenVPN-UI-Certs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d3vilh/openvpn-aws/HEAD/images/OpenVPN-UI-Certs.png -------------------------------------------------------------------------------- /images/OpenVPN-UI-Home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d3vilh/openvpn-aws/HEAD/images/OpenVPN-UI-Home.png -------------------------------------------------------------------------------- /images/OpenVPN-UI-Login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d3vilh/openvpn-aws/HEAD/images/OpenVPN-UI-Login.png -------------------------------------------------------------------------------- /images/OpenVPN-UI-Logs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d3vilh/openvpn-aws/HEAD/images/OpenVPN-UI-Logs.png -------------------------------------------------------------------------------- /ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | nocows = True 3 | inventory = ./inventory.yml 4 | interpreter_python = auto_silent 5 | -------------------------------------------------------------------------------- /images/OVPN_ext_serv_ip1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d3vilh/openvpn-aws/HEAD/images/OVPN_ext_serv_ip1.png -------------------------------------------------------------------------------- /images/OVPN_ext_serv_ip2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d3vilh/openvpn-aws/HEAD/images/OVPN_ext_serv_ip2.png -------------------------------------------------------------------------------- /images/OpenVPN-UI-Config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d3vilh/openvpn-aws/HEAD/images/OpenVPN-UI-Config.png -------------------------------------------------------------------------------- /images/OpenVPN-UI-Profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d3vilh/openvpn-aws/HEAD/images/OpenVPN-UI-Profile.png -------------------------------------------------------------------------------- /images/OpenVPN-UI-Restart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d3vilh/openvpn-aws/HEAD/images/OpenVPN-UI-Restart.png -------------------------------------------------------------------------------- /images/OpenVPN-UI-Revoke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d3vilh/openvpn-aws/HEAD/images/OpenVPN-UI-Revoke.png -------------------------------------------------------------------------------- /images/OVPN_Palm_connected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d3vilh/openvpn-aws/HEAD/images/OVPN_Palm_connected.png -------------------------------------------------------------------------------- /images/OpenVPN-EC2-OVPN-Only.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d3vilh/openvpn-aws/HEAD/images/OpenVPN-EC2-OVPN-Only.png -------------------------------------------------------------------------------- /images/OpenVPN-EC2-UI-Only.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d3vilh/openvpn-aws/HEAD/images/OpenVPN-EC2-UI-Only.png -------------------------------------------------------------------------------- /openvpn-server/db/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | collections: 3 | - ansible.posix 4 | - community.docker 5 | - community.general 6 | -------------------------------------------------------------------------------- /openvpn-server/clients/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /openvpn-server/pki/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /images/OVPN_New_Client_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d3vilh/openvpn-aws/HEAD/images/OVPN_New_Client_download.png -------------------------------------------------------------------------------- /images/OpenVPN-UI-Server-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d3vilh/openvpn-aws/HEAD/images/OpenVPN-UI-Server-config.png -------------------------------------------------------------------------------- /openvpn-server/staticclients/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /templates/grafana-config.monitoring.j2: -------------------------------------------------------------------------------- 1 | GF_SECURITY_ADMIN_PASSWORD={{ monitoring_grafana_admin_password }} 2 | GF_USERS_ALLOW_SIGN_UP=false 3 | GF_INSTALL_PLUGINS=flant-statusmap-panel,ae3e-plotly-panel -------------------------------------------------------------------------------- /monitoring/grafana/provisioning/dashboards/dashboard.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: 1 3 | 4 | providers: 5 | - name: 'prometheus' 6 | orgId: 1 7 | folder: '' 8 | type: file 9 | disableDeletion: false 10 | editable: true 11 | options: 12 | path: /etc/grafana/provisioning/dashboards 13 | -------------------------------------------------------------------------------- /templates/prometheus.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | global: 3 | scrape_interval: 15s 4 | evaluation_interval: 15s 5 | scrape_timeout: 10s 6 | external_labels: 7 | monitor: 'Alertmanager' 8 | 9 | rule_files: 10 | - 'alert.rules' 11 | 12 | scrape_configs: 13 | - job_name: 'ovpn_exporter' 14 | metrics_path: /metrics 15 | scrape_interval: 1m 16 | static_configs: 17 | - targets: ['ovpn_exporter:9176'] -------------------------------------------------------------------------------- /tasks/handlers.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Restart openvpn 3 | community.docker.docker_compose: 4 | project_src: "{{ config_dir }}/openvpn-server/" 5 | build: false 6 | restarted: true 7 | become: false 8 | when: ansible_distribution != "Ubuntu" 9 | 10 | - name: Restart monitoring 11 | community.docker.docker_compose: 12 | project_src: "{{ config_dir }}/monitoring/" 13 | build: false 14 | restarted: true 15 | become: false 16 | when: ansible_distribution != "Ubuntu" -------------------------------------------------------------------------------- /tasks/handlers_ubuntu.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Restart openvpn (Ubuntu) 3 | community.docker.docker_compose_v2: 4 | project_src: "{{ config_dir }}/openvpn-server/" 5 | build: never 6 | state: restarted 7 | become: false 8 | when: ansible_distribution == "Ubuntu" 9 | 10 | - name: Restart monitoring (Ubuntu) 11 | community.docker.docker_compose_v2: 12 | project_src: "{{ config_dir }}/monitoring/" 13 | build: never 14 | state: restarted 15 | become: false 16 | when: ansible_distribution == "Ubuntu" and monitoring_enable -------------------------------------------------------------------------------- /templates/openvpn_client.conf.j2: -------------------------------------------------------------------------------- 1 | client 2 | dev tun 3 | proto udp 4 | {{ ovpn_remote }} 5 | resolv-retry infinite 6 | nobind 7 | user nobody 8 | group nogroup 9 | persist-key 10 | persist-tun 11 | remote-cert-tls server 12 | # cipher AES-256-CBC # Deprecated since v.0.3. we are using GCM now. 13 | cipher AES-256-GCM 14 | auth SHA512 15 | auth-nocache 16 | tls-client 17 | tls-version-min 1.2 18 | tls-cipher TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384:TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-GCM-SHA384 19 | verb 3 20 | explicit-exit-notify 21 | key-direction 1 22 | -------------------------------------------------------------------------------- /inventory.yml: -------------------------------------------------------------------------------- 1 | all: 2 | hosts: 3 | openvpn-aws: 4 | # Section One: 5 | ansible_host: 192.168.1.1 6 | ansible_user: admin 7 | ansible_connection: local 8 | ansible_use: "yes" 9 | # Section Two: 10 | #ansible_host: 192.168.1.1 11 | #ansible_user: admin 12 | #ansible_connection: ssh 13 | #ansible_use: "yes" 14 | 15 | # Comment out "Section One" parameters and uncomment "Section Two" to run on remote OpenVPN-AWS enviroment. 16 | # Double check the ansible_user and ansible_host. 17 | -------------------------------------------------------------------------------- /tasks/debian-libseccomp-update.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Add Buster backports apt key. 3 | ansible.builtin.apt_key: 4 | keyserver: hkp://keyserver.ubuntu.com:80 5 | id: '{{ item }}' 6 | loop: 7 | - 04EE7237B7D453EC 8 | - 648ACFD622F3D138 9 | 10 | - name: Add Buster backports for fixed libseccomp2. 11 | ansible.builtin.apt_repository: 12 | repo: deb http://httpredir.debian.org/debian buster-backports main contrib non-free 13 | state: present 14 | filename: debian-backports 15 | 16 | - name: Install >libseccomp2.4.4 to fix 32-bit OS issue. 17 | ansible.builtin.apt: 18 | name: libseccomp2 19 | state: latest # noqa package-latest 20 | default_release: buster-backports 21 | -------------------------------------------------------------------------------- /monitoring/prometheus/alert.rules: -------------------------------------------------------------------------------- 1 | groups: 2 | - name: example 3 | rules: 4 | 5 | # Alert for any instance that is unreachable for >5 minutes. 6 | - alert: service_down 7 | expr: up == 0 8 | for: 2m 9 | labels: 10 | severity: page 11 | annotations: 12 | summary: "Instance {{ $labels.instance }} down" 13 | description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 2 minutes." 14 | 15 | - alert: high_load 16 | expr: node_load1 > 0.5 17 | for: 2m 18 | labels: 19 | severity: page 20 | annotations: 21 | summary: "Instance {{ $labels.instance }} under high load" 22 | description: "{{ $labels.instance }} of job {{ $labels.job }} is under high load." 23 | -------------------------------------------------------------------------------- /templates/easy-rsa.vars.j2: -------------------------------------------------------------------------------- 1 | set_var EASYRSA_DN "{{ easyrsa_dn }}" 2 | set_var EASYRSA_REQ_COUNTRY "{{ easyrsa_req_country }}" 3 | set_var EASYRSA_REQ_PROVINCE "{{ easyrsa_req_province }}" 4 | set_var EASYRSA_REQ_CITY "{{ easyrsa_req_city }}" 5 | set_var EASYRSA_REQ_ORG "{{ easyrsa_req_org }}" 6 | set_var EASYRSA_REQ_EMAIL "{{ easyrsa_req_email }}" 7 | set_var EASYRSA_REQ_OU "{{ easyrsa_req_ou }}" 8 | set_var EASYRSA_REQ_CN "{{ easyrsa_req_cn }}" 9 | set_var EASYRSA_KEY_SIZE {{ easyrsa_key_size }} 10 | set_var EASYRSA_CA_EXPIRE {{ easyrsa_ca_expire }} 11 | set_var EASYRSA_CERT_EXPIRE {{ easyrsa_cert_expire }} 12 | set_var EASYRSA_CERT_RENEW {{ easyrsa_cert_renew }} 13 | set_var EASYRSA_CRL_DAYS {{ easyrsa_crl_days }} -------------------------------------------------------------------------------- /monitoring/grafana/provisioning/datasources/datasource.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: 1 3 | 4 | # list of datasources that should be deleted from the database 5 | deleteDatasources: 6 | - name: prometheus 7 | orgId: 1 8 | 9 | # list of datasources to insert/update depending 10 | # whats available in the database 11 | datasources: 12 | - name: prometheus 13 | type: prometheus 14 | access: proxy 15 | orgId: 1 16 | url: http://prometheus:9090 17 | password: 18 | user: 19 | database: 20 | basicAuth: true 21 | basicAuthUser: admin 22 | basicAuthPassword: foobar 23 | withCredentials: 24 | isDefault: 25 | jsonData: 26 | graphiteVersion: "1.1" 27 | tlsAuth: false 28 | tlsAuthWithCACert: false 29 | secureJsonData: 30 | tlsCACert: "..." 31 | tlsClientCert: "..." 32 | tlsClientKey: "..." 33 | version: 1 34 | editable: true 35 | -------------------------------------------------------------------------------- /tasks/docker_ubuntu.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Check if Docker is already present. 3 | ansible.builtin.command: which docker 4 | failed_when: false 5 | changed_when: false 6 | check_mode: false 7 | register: docker_command_result 8 | 9 | - name: Install Docker and Docker Compose v2 (Ubuntu). 10 | ansible.builtin.apt: 11 | name: 12 | - docker-compose-v2 13 | state: present 14 | when: ansible_distribution == "Ubuntu" 15 | 16 | - name: Ensure Docker is started. 17 | ansible.builtin.service: 18 | name: docker 19 | state: started 20 | enabled: true 21 | 22 | - name: Ensure {{ ansible_user }} user is added to the docker group. 23 | ansible.builtin.user: 24 | name: "{{ ansible_user }}" 25 | groups: docker 26 | append: true 27 | 28 | # reset_connection doesn't support conditionals. 29 | - name: Reset connection so docker group is picked up. 30 | meta: reset_connection 31 | -------------------------------------------------------------------------------- /openvpn-server/openvpn-docker/bin/oath-sec-gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # v.0.1 by @d3vilh@github.com aka Mr. Philipp 3 | # d3vilh/openvpn-server drafted 2FA support 4 | 5 | # Client name in format: alice@wonderland.ua 6 | TFA_NAME=$1 7 | OPENVPN_DIR=/etc/openvpn 8 | OATH_SECRETS=$OPENVPN_DIR/clients/oath.secrets 9 | 10 | # Issuer string 11 | ISSUER='MFA%20OpenVPN' 12 | 13 | # Userhash. Random 30 chars 14 | USERHASH=$(head -c 10 /dev/urandom | openssl sha256 | cut -d ' ' -f2 | cut -b 1-30) 15 | 16 | # Base32 secret from oathtool output 17 | BASE32=$(/usr/bin/oathtool --totp -v "$USERHASH" | grep Base32 | awk '{print $3}') 18 | 19 | QRSTRING="otpauth://totp/$ISSUER:$TFA_NAME?secret=$BASE32" 20 | # QR code for user to pass to Google Authenticator or OpenVPN-UI 21 | echo "User String for QR:" 22 | echo $QRSTRING 23 | 24 | qrencode $QRSTRING -o $OPENVPN_DIR/clients/$TFA_NAME.png 25 | 26 | # New string for secrets file 27 | echo "oath.secrets entry for BackEnd:" 28 | echo "$TFA_NAME:$USERHASH" | tee -a $OATH_SECRETS 29 | -------------------------------------------------------------------------------- /tasks/openvpn_exporter.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Copy OpenVPN dashboard configs to Grafana. 3 | ansible.builtin.copy: 4 | src: templates/openvpn-dashboard.json.j2 5 | dest: "{{ config_dir }}/monitoring/grafana/provisioning/dashboards/openvpn.json" 6 | mode: '0644' 7 | become: false 8 | notify: Restart monitoring 9 | 10 | - name: Create OpenVPN exporter folder on Pi. 11 | ansible.builtin.file: 12 | path: "{{ config_dir }}/openvpn-server/openvpn_exporter" 13 | state: directory 14 | mode: 0755 15 | become: false 16 | 17 | - name: Copy OpenVPN exporter template to Pi. 18 | ansible.builtin.template: 19 | src: templates/openvpn_exporter.yml.j2 20 | dest: "{{ config_dir }}/openvpn-server/openvpn_exporter/docker-compose.yml" 21 | mode: '0640' 22 | become: false 23 | notify: Restart monitoring 24 | 25 | - name: Ensure OpenVPN Exporter is running. 26 | community.docker.docker_compose: 27 | project_src: "{{ config_dir }}/openvpn-server/openvpn_exporter/" 28 | build: false 29 | become: false 30 | -------------------------------------------------------------------------------- /openvpn-server/openvpn-docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # Start from Alpine base image 2 | FROM alpine 3 | LABEL maintainer="Mr.Philipp " 4 | LABEL version="0.4" 5 | 6 | # Set the working directory to /opt/app 7 | WORKDIR /opt/app 8 | 9 | RUN apk --no-cache --no-progress upgrade && apk --no-cache --no-progress add bash bind-tools oath-toolkit oath-toolkit-oathtool curl ip6tables iptables openvpn easy-rsa 10 | 11 | #Install Latest RasyRSA Version 12 | RUN chmod 755 /usr/share/easy-rsa/* 13 | 14 | # Copy all files in the current directory to the /opt/app directory in the container 15 | COPY bin /opt/app/bin 16 | COPY docker-entrypoint.sh /opt/app/docker-entrypoint.sh 17 | RUN mkdir -p /opt/app/clients \ 18 | /opt/app/db \ 19 | /opt/app/log \ 20 | /opt/app/pki \ 21 | /opt/app/staticclients 22 | 23 | # Add the openssl-easyrsa.cnf file to the easy-rsa directory 24 | ADD openssl-easyrsa.cnf /opt/app/easy-rsa/ 25 | 26 | # Make all files in the bin directory executable 27 | RUN chmod +x bin/*; chmod +x docker-entrypoint.sh 28 | 29 | # Expose the OpenVPN port (1194/udp) 30 | EXPOSE 1194/udp 31 | 32 | # Set the entrypoint to the docker-entrypoint.sh script, passing in the following arguments: 33 | # $REQ_COUNTRY $REQ_PROVINCE $REQ_CITY $REQ_ORG $REQ_OU $REQ_CN 34 | ENTRYPOINT ./docker-entrypoint.sh 35 | -------------------------------------------------------------------------------- /openvpn-server/server.conf: -------------------------------------------------------------------------------- 1 | management 0.0.0.0 2080 2 | 3 | port 1194 4 | proto udp 5 | 6 | dev tun 7 | 8 | ca pki/ca.crt 9 | cert pki/issued/server.crt 10 | key pki/private/server.key 11 | 12 | # cipher AES-256-CBC # Deprecated since v.0.3. we are using GCM now. 13 | cipher AES-256-GCM 14 | auth SHA512 15 | dh pki/dh.pem 16 | 17 | server 10.0.70.0 255.255.255.0 18 | route 10.0.71.0 255.255.255.0 19 | ifconfig-pool-persist pki/ipp.txt 20 | push "route 10.0.60.0 255.255.255.0" 21 | push "dhcp-option DNS 8.8.8.8" 22 | push "dhcp-option DNS 1.0.0.1" 23 | 24 | keepalive 10 120 25 | max-clients 100 26 | 27 | persist-key 28 | persist-tun 29 | 30 | log /var/log/openvpn/openvpn.log 31 | verb 4 32 | topology subnet 33 | 34 | client-config-dir /etc/openvpn/staticclients 35 | push "redirect-gateway def1 bypass-dhcp" 36 | 37 | #ncp-ciphers AES-256-GCM:AES-192-GCM:AES-128-GCM # Deprecated since v.0.3. we have to use data-ciphers below instead 38 | data-ciphers AES-256-GCM:AES-192-GCM:AES-128-GCM 39 | 40 | user nobody 41 | group nogroup 42 | 43 | status-version 2 44 | status /var/log/openvpn/openvpn-status.log 45 | explicit-exit-notify 1 46 | crl-verify pki/crl.pem 47 | 48 | # 2FA Auth part 49 | # script-security 2 50 | # auth-user-pass-verify /opt/app/bin/oath.sh via-file 51 | 52 | # Default openvpn-server configuration file -------------------------------------------------------------------------------- /openvpn-server/config/old-server.conf: -------------------------------------------------------------------------------- 1 | management 0.0.0.0 2080 2 | 3 | port 1194 4 | proto udp 5 | 6 | dev tun 7 | 8 | ca pki/ca.crt 9 | cert pki/issued/server.crt 10 | key pki/private/server.key 11 | 12 | # cipher AES-256-CBC # Deprecated since v.0.3. we are using GCM now. 13 | cipher AES-256-GCM 14 | auth SHA512 15 | dh pki/dh.pem 16 | 17 | server 10.0.70.0 255.255.255.0 18 | route 10.0.71.0 255.255.255.0 19 | ifconfig-pool-persist pki/ipp.txt 20 | push "route 10.0.60.0 255.255.255.0" 21 | push "dhcp-option DNS 8.8.8.8" 22 | push "dhcp-option DNS 1.0.0.1" 23 | 24 | keepalive 10 120 25 | max-clients 100 26 | 27 | persist-key 28 | persist-tun 29 | 30 | log /var/log/openvpn/openvpn.log 31 | verb 4 32 | topology subnet 33 | 34 | client-config-dir /etc/openvpn/staticclients 35 | push "redirect-gateway def1 bypass-dhcp" 36 | 37 | #ncp-ciphers AES-256-GCM:AES-192-GCM:AES-128-GCM # Deprecated since v.0.3. we have to use data-ciphers below instead 38 | data-ciphers AES-256-GCM:AES-192-GCM:AES-128-GCM 39 | 40 | user nobody 41 | group nogroup 42 | 43 | status-version 2 44 | status /var/log/openvpn/openvpn-status.log 45 | explicit-exit-notify 1 46 | crl-verify pki/crl.pem 47 | 48 | # 2FA Auth part 49 | # script-security 2 50 | # auth-user-pass-verify /opt/app/bin/oath.sh via-file 51 | 52 | # Default openvpn-server configuration file -------------------------------------------------------------------------------- /tasks/openvpn.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create OpenVPN folder on Pi. 3 | ansible.builtin.file: 4 | path: "{{ config_dir }}/openvpn-server" 5 | state: directory 6 | mode: 0755 7 | become: false 8 | 9 | - name: Synchronize openvpn directory. 10 | ansible.posix.synchronize: 11 | src: openvpn-server 12 | dest: "{{ config_dir }}/" 13 | delete: false 14 | recursive: true 15 | perms: false 16 | become: false 17 | 18 | - name: Copy OpenVPN docker-compose template to Pi. 19 | ansible.builtin.template: 20 | src: templates/{{ item.src }} 21 | dest: "{{ config_dir }}/openvpn-server/{{ item.dest }}" 22 | mode: 0740 23 | loop: 24 | - src: openvpn-docker-compose.yml.j2 25 | dest: docker-compose.yml 26 | - src: openvpn-docker-entrypoint.sh.j2 27 | dest: openvpn-docker/docker-entrypoint.sh 28 | - src: openvpn_client.conf.j2 29 | dest: config/client.conf 30 | - src: easy-rsa.vars.j2 31 | dest: config/easy-rsa.vars 32 | become: false 33 | notify: 34 | - Restart openvpn 35 | when: ansible_distribution != "Ubuntu" 36 | 37 | # TODO: The first time this playbook is run, the `pi` user may not be added 38 | # to the `docker` group, so this task may fail. 39 | - name: Ensure OpenVPN is running. 40 | community.docker.docker_compose: 41 | project_src: "{{ config_dir }}/openvpn-server/" 42 | build: false 43 | become: false 44 | when: ansible_distribution != "Ubuntu" 45 | 46 | -------------------------------------------------------------------------------- /openvpn-server/openvpn-docker/bin/oath.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # v.0.1 by @d3vilh@github.com aka Mr. Philipp 3 | # d3vilh/openvpn-server drafted 2FA support 4 | # 5 | # MFA verification by OpenVPN server using oath-tool 6 | echo "string oath.sh" 7 | 8 | # VARIABLES 9 | PASSFILE=$1 # Password file passed by openvpn-server with "auth-user-pass-verify /opt/app/bin/oath.sh via-file" in server.conf 10 | OPENVPN_DIR=/etc/openvpn 11 | OATH_SECRETS=$OPENVPN_DIR/clients/oath.secrets 12 | LOG_FILE=/var/log/openvpn/oath.log 13 | 14 | echo -e "$(date) Openvpn dir: $OPENVPN_DIR\nOath secrets: $OATH_SECRETS\nLog file: $LOG_FILE\nPassfile: $PASSFILE\n" | tee -a $LOG_FILE 15 | 16 | # Geting user and password passed by external user to OpenVPN server tmp file 17 | user=$(head -1 $PASSFILE) 18 | pass=$(tail -1 $PASSFILE) 19 | 20 | echo "$(date) - Authentication attempt for user $user" | tee -a $LOG_FILE # echo "$(date) - Password: $pass" | tee -a $LOG_FILE 21 | 22 | # Parsing oath.secrets to getting secret entry, ignore case 23 | secret=$(grep -i -m 1 "$user:" $OATH_SECRETS | cut -d: -f2) # echo "$(date) - Secret: $secret" | tee -a $LOG_FILE 24 | 25 | # Getting 2FA code with oathtool based on our secret, exiting with 0 if match: 26 | code=$(oathtool --totp $secret) # echo "$(date) - Code: $code" | tee -a $LOG_FILE 27 | 28 | if [ "$code" = "$pass" ]; 29 | then 30 | echo "OK" 31 | exit 0 32 | else 33 | echo "FAIL" 34 | fi 35 | 36 | # If we make it here, auth hasn't succeeded, don't grant access 37 | echo "$(date) - Authentication failed for user $user" | tee -a $LOG_FILE 38 | exit 1 -------------------------------------------------------------------------------- /tasks/openvpn_ubuntu.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create OpenVPN folder on Pi. 3 | ansible.builtin.file: 4 | path: "{{ config_dir }}/openvpn-server" 5 | state: directory 6 | mode: 0755 7 | become: false 8 | 9 | - name: Synchronize openvpn directory. 10 | ansible.posix.synchronize: 11 | src: openvpn-server 12 | dest: "{{ config_dir }}/" 13 | delete: false 14 | recursive: true 15 | perms: false 16 | become: false 17 | 18 | - name: Copy OpenVPN docker-compose template to Pi. 19 | ansible.builtin.template: 20 | src: templates/{{ item.src }} 21 | dest: "{{ config_dir }}/openvpn-server/{{ item.dest }}" 22 | mode: 0740 23 | loop: 24 | - src: openvpn-docker-compose.yml.j2 25 | dest: docker-compose.yml 26 | - src: openvpn-docker-entrypoint.sh.j2 27 | dest: openvpn-docker/docker-entrypoint.sh 28 | - src: openvpn_client.conf.j2 29 | dest: config/client.conf 30 | - src: easy-rsa.vars.j2 31 | dest: config/easy-rsa.vars 32 | become: false 33 | when: ansible_distribution == "Ubuntu" 34 | 35 | # TODO: The first time this playbook is run, the `pi` user may not be added 36 | # to the `docker` group, so this task may fail. 37 | - name: Ensure OpenVPN is running (Ubuntu). 38 | community.docker.docker_compose_v2: 39 | project_src: "{{ config_dir }}/openvpn-server/" 40 | build: never 41 | become: false 42 | when: ansible_distribution == "Ubuntu" 43 | 44 | - name: Include handlers (Ubuntu) 45 | ansible.builtin.import_tasks: tasks/handlers_ubuntu.yml 46 | when: ansible_distribution == "Ubuntu" -------------------------------------------------------------------------------- /templates/openvpn_exporter-docker-compose.yml.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | --- 3 | version: "3.5" 4 | 5 | volumes: 6 | prometheus_data: {} 7 | grafana_data: {} 8 | 9 | networks: 10 | front-tier: 11 | back-tier: 12 | 13 | services: 14 | prometheus: 15 | container_name: prometheus 16 | image: prom/prometheus:latest 17 | restart: always 18 | command: 19 | - '--config.file=/etc/prometheus/prometheus.yml' 20 | - '--storage.tsdb.path=/prometheus' 21 | - '--storage.tsdb.retention.time={{ monitoring_days_keep_interval }}' 22 | - '--web.console.libraries=/usr/share/prometheus/console_libraries' 23 | - '--web.console.templates=/usr/share/prometheus/consoles' 24 | volumes: 25 | - ./prometheus/:/etc/prometheus/ 26 | - prometheus_data:/prometheus 27 | ports: 28 | - 9090:9090 29 | networks: 30 | - back-tier 31 | 32 | grafana: 33 | container_name: grafana 34 | image: grafana/grafana 35 | restart: always 36 | env_file: 37 | - ./grafana/config.monitoring 38 | volumes: 39 | - grafana_data:/var/lib/grafana 40 | - ./grafana/provisioning/:/etc/grafana/provisioning/ 41 | depends_on: 42 | - prometheus 43 | ports: 44 | - 3030:3000 45 | networks: 46 | - back-tier 47 | - front-tier 48 | 49 | ovpn_exporter: 50 | container_name: ovpn_exporter 51 | image: d3vilh/openvpn_exporter:latest 52 | restart: always 53 | privileged: true 54 | environment: 55 | - OVPN_STATUS_FILE=/etc/openvpn/log/openvpn-status.log 56 | volumes: 57 | - ../openvpn-server/log:/etc/openvpn/log 58 | ports: 59 | - "9176:9176/tcp" 60 | networks: 61 | - back-tier -------------------------------------------------------------------------------- /templates/openvpn-docker-compose.yml.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | --- 3 | version: "3.5" 4 | 5 | services: 6 | openvpn: 7 | container_name: openvpn 8 | # If you want to build your own image, uncomment the following line and comment the image line 9 | #build: ./openvpn-docker 10 | image: d3vilh/openvpn-server:latest 11 | privileged: true 12 | ports: 13 | - "1194:1194/udp" 14 | # - "2080:2080/tcp" # management port. uncomment if you would like to share it with the host 15 | environment: 16 | TRUST_SUB: {{ ovpn_trusted_subnet }} 17 | GUEST_SUB: {{ ovpn_guest_subnet }} 18 | HOME_SUB: {{ ovpn_home_subnet }} 19 | volumes: 20 | - ./pki:/etc/openvpn/pki 21 | - ./clients:/etc/openvpn/clients 22 | - ./config:/etc/openvpn/config 23 | - ./staticclients:/etc/openvpn/staticclients 24 | - ./log:/var/log/openvpn 25 | - ./fw-rules.sh:/opt/app/fw-rules.sh 26 | - ./checkpsw.sh:/opt/app/checkpsw.sh 27 | - ./server.conf:/etc/openvpn/server.conf 28 | cap_add: 29 | - NET_ADMIN 30 | restart: always 31 | depends_on: 32 | - "openvpn-ui" 33 | 34 | openvpn-ui: 35 | container_name: openvpn-ui 36 | image: d3vilh/openvpn-ui:latest 37 | environment: 38 | - OPENVPN_ADMIN_USERNAME={{ ovpnui_user }} 39 | - OPENVPN_ADMIN_PASSWORD={{ ovpnui_password }} 40 | privileged: true 41 | ports: 42 | - "8080:8080/tcp" 43 | volumes: 44 | - ./:/etc/openvpn 45 | - ./db:/opt/openvpn-ui/db 46 | - ./pki:/usr/share/easy-rsa/pki 47 | - /var/run/docker.sock:/var/run/docker.sock:ro 48 | restart: always 49 | -------------------------------------------------------------------------------- /tasks/docker.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Check if Docker is already present. 3 | ansible.builtin.command: which docker 4 | failed_when: false 5 | changed_when: false 6 | check_mode: false 7 | register: docker_command_result 8 | 9 | - name: Download Docker install convenience script. 10 | ansible.builtin.get_url: 11 | url: https://get.docker.com/ 12 | dest: /tmp/get-docker.sh 13 | mode: 0775 14 | when: 15 | - docker_command_result.rc == 1 16 | 17 | - name: Run Docker install convenience script. 18 | ansible.builtin.command: /tmp/get-docker.sh 19 | environment: 20 | CHANNEL: stable 21 | when: 22 | - docker_command_result.rc == 1 23 | 24 | - name: Ensure Docker is started. 25 | ansible.builtin.service: 26 | name: docker 27 | state: started 28 | enabled: true 29 | 30 | - name: Ensure dependencies are installed (Debian). 31 | ansible.builtin.apt: 32 | name: 33 | - libffi-dev 34 | - libssl-dev 35 | - python3-dev 36 | - python3-pip 37 | - python3-docker 38 | - docker-compose 39 | - git 40 | state: present 41 | when: ansible_facts.os_family == "Debian" 42 | 43 | - name: Ensure dependencies are installed (Archlinux). 44 | community.general.pacman: 45 | name: 46 | - libffi 47 | - openssl 48 | - base-devel 49 | - python-pip 50 | - python3-docker 51 | - docker-compose 52 | - git 53 | state: present 54 | when: ansible_facts.os_family == "Archlinux" 55 | 56 | #- name: Install Docker Compose using Pip. 57 | # ansible.builtin.pip: 58 | # name: docker-compose 59 | # state: present 60 | # executable: pip3 61 | 62 | - name: Ensure {{ ansible_user }} user is added to the docker group. 63 | ansible.builtin.user: 64 | name: "{{ ansible_user }}" 65 | groups: docker 66 | append: true 67 | 68 | # reset_connection doesn't support conditionals. 69 | - name: Reset connection so docker group is picked up. 70 | meta: reset_connection 71 | -------------------------------------------------------------------------------- /main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: openvpn-aws 3 | become: true 4 | 5 | pre_tasks: 6 | - name: Load configuration (with defaults from example file). 7 | ansible.builtin.include_vars: "{{ item }}" 8 | loop: 9 | - example.config.yml 10 | - config.yml 11 | 12 | - name: Ensure apt cache is up to date. 13 | ansible.builtin.apt: 14 | update_cache: true 15 | cache_valid_time: 3600 16 | when: 17 | - ansible_facts.os_family == "Debian" 18 | 19 | - name: Ensure pacman cache is up to date 20 | community.general.pacman: 21 | update_cache: true 22 | when: 23 | - ansible_facts.os_family == "Archlinux" 24 | 25 | handlers: 26 | - name: Include handlers 27 | ansible.builtin.import_tasks: tasks/handlers.yml 28 | when: ansible_distribution != "Ubuntu" 29 | 30 | tasks: 31 | - name: Setup Docker. 32 | ansible.builtin.import_tasks: tasks/docker.yml 33 | when: 34 | - ansible_distribution != "Ubuntu" 35 | 36 | - name: Set up OpenVPN. 37 | ansible.builtin.import_tasks: tasks/openvpn.yml 38 | when: 39 | - ovpn_enable 40 | - ansible_distribution != "Ubuntu" 41 | 42 | - name: Set up OpenVPN Monitoring. 43 | ansible.builtin.import_tasks: tasks/openvpn_monitoring.yml 44 | when: 45 | - monitoring_enable 46 | - ansible_distribution != "Ubuntu" 47 | 48 | - name: Setup Docker (Ubuntu). 49 | ansible.builtin.include_tasks: tasks/docker_ubuntu.yml 50 | when: 51 | - ansible_distribution == "Ubuntu" 52 | 53 | - name: Set up OpenVPN (Ubuntu). 54 | ansible.builtin.include_tasks: tasks/openvpn_ubuntu.yml 55 | when: 56 | - ovpn_enable 57 | - ansible_distribution == "Ubuntu" 58 | 59 | - name: Set up OpenVPN Monitoring (Ubuntu). 60 | ansible.builtin.include_tasks: tasks/openvpn_monitoring_ubuntu.yml 61 | when: 62 | - monitoring_enable 63 | - ansible_distribution == "Ubuntu" -------------------------------------------------------------------------------- /tasks/openvpn_monitoring.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Gather package facts. 3 | ansible.builtin.package_facts: 4 | manager: auto 5 | when: ansible_facts.userspace_bits == '32' 6 | 7 | - name: Upgrade libseccomp2 to latest version (32-bit Debian). 8 | ansible.builtin.import_tasks: tasks/debian-libseccomp-update.yml 9 | when: 10 | - ansible_facts.os_family == "Debian" 11 | - ansible_facts.userspace_bits == '32' 12 | - ansible_facts.packages['libseccomp2'][0]['version'] is version('2.4.4', '<') 13 | 14 | - name: Synchronize monitoring directory. 15 | ansible.posix.synchronize: 16 | src: monitoring 17 | dest: "{{ config_dir }}/" 18 | delete: false 19 | recursive: true 20 | perms: false 21 | become: false 22 | 23 | - name: Ensure monitoring directory is not a Git repository. 24 | ansible.builtin.file: 25 | path: "{{ config_dir }}/monitoring/.git/" 26 | state: absent 27 | become: false 28 | 29 | - name: Copy templated monitoring files into place. 30 | ansible.builtin.template: 31 | src: templates/{{ item.src }} 32 | dest: "{{ config_dir }}/monitoring/{{ item.dest }}" 33 | mode: 0644 34 | loop: 35 | - src: grafana-config.monitoring.j2 36 | dest: grafana/config.monitoring 37 | - src: prometheus.yml.j2 38 | dest: prometheus/prometheus.yml 39 | - src: openvpn_exporter-docker-compose.yml.j2 40 | dest: docker-compose.yml 41 | notify: Restart monitoring 42 | become: false 43 | 44 | - name: Copy OpenVPN monitoring dashboard config to Grafana. 45 | ansible.builtin.copy: 46 | src: templates/openvpn-dashboard.json.j2 47 | dest: "{{ config_dir }}/monitoring/grafana/provisioning/dashboards/openvpn.json" 48 | mode: '0644' 49 | become: false 50 | notify: Restart monitoring 51 | when: monitoring_enable 52 | 53 | # WA for 60sec TMO bug 54 | - name: Pull latest Grafana Docker image 55 | community.general.docker_image: 56 | source: pull 57 | name: grafana/grafana 58 | tag: latest 59 | become: false 60 | 61 | - name: Ensure monitoring environment is running. 62 | community.docker.docker_compose: 63 | project_src: "{{ config_dir }}/monitoring/" 64 | build: false 65 | become: false 66 | environment: 67 | DOCKER_CLIENT_TIMEOUT: '180' 68 | COMPOSE_HTTP_TIMEOUT: '180' -------------------------------------------------------------------------------- /tasks/openvpn_monitoring_ubuntu.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Gather package facts. 3 | ansible.builtin.package_facts: 4 | manager: auto 5 | when: ansible_facts.userspace_bits == '32' 6 | 7 | - name: Upgrade libseccomp2 to latest version (32-bit Debian). 8 | ansible.builtin.import_tasks: tasks/debian-libseccomp-update.yml 9 | when: 10 | - ansible_facts.os_family == "Debian" 11 | - ansible_facts.userspace_bits == '32' 12 | - ansible_facts.packages['libseccomp2'][0]['version'] is version('2.4.4', '<') 13 | 14 | - name: Synchronize monitoring directory. 15 | ansible.posix.synchronize: 16 | src: monitoring 17 | dest: "{{ config_dir }}/" 18 | delete: false 19 | recursive: true 20 | perms: false 21 | become: false 22 | 23 | - name: Ensure monitoring directory is not a Git repository. 24 | ansible.builtin.file: 25 | path: "{{ config_dir }}/monitoring/.git/" 26 | state: absent 27 | become: false 28 | 29 | - name: Copy templated monitoring files into place. 30 | ansible.builtin.template: 31 | src: templates/{{ item.src }} 32 | dest: "{{ config_dir }}/monitoring/{{ item.dest }}" 33 | mode: 0644 34 | loop: 35 | - src: grafana-config.monitoring.j2 36 | dest: grafana/config.monitoring 37 | - src: prometheus.yml.j2 38 | dest: prometheus/prometheus.yml 39 | - src: openvpn_exporter-docker-compose.yml.j2 40 | dest: docker-compose.yml 41 | notify: Restart monitoring (Ubuntu) 42 | become: false 43 | 44 | - name: Copy OpenVPN monitoring dashboard config to Grafana. 45 | ansible.builtin.copy: 46 | src: templates/openvpn-dashboard.json.j2 47 | dest: "{{ config_dir }}/monitoring/grafana/provisioning/dashboards/openvpn.json" 48 | mode: '0644' 49 | become: false 50 | notify: Restart monitoring (Ubuntu) 51 | when: monitoring_enable 52 | 53 | # WA for 60sec TMO bug 54 | - name: Pull latest Grafana Docker image 55 | community.general.docker_image: 56 | source: pull 57 | name: grafana/grafana 58 | tag: latest 59 | become: false 60 | 61 | - name: Ensure monitoring environment is running. 62 | community.docker.docker_compose_v2: 63 | project_src: "{{ config_dir }}/monitoring/" 64 | build: never 65 | become: false 66 | environment: 67 | DOCKER_CLIENT_TIMEOUT: '180' 68 | COMPOSE_HTTP_TIMEOUT: '180' -------------------------------------------------------------------------------- /config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Location where configuration files will be stored. 3 | config_dir: '~' 4 | 5 | # OpenVPN configuration. 6 | ovpn_enable: true 7 | 8 | ovpnui_user: "aws-admin" 9 | ovpnui_password: "gagaZush" # !Change this password! 10 | ovpn_trusted_subnet: "10.0.70.0/24" # Trusted users subnet 11 | ovpn_guest_subnet: "10.0.71.0/24" # Guest users subnet 12 | ovpn_home_subnet: "192.168.88.0/24" # Your home network subnet 13 | ovpn_remote: "remote 123.124.125.126 1194 udp" # OpenVPN client.ovpn profile connect line 14 | 15 | # EasyRSA configuration parameters. 16 | easyrsa_dn: "org" # Leave this as-is. "org" for traditional, "cn_only" for CN only. 17 | easyrsa_req_country: "UA" # The two-letter country code (e.g. US). 18 | easyrsa_req_province: "KY" # The two-letter state or province code (e.g. CA). 19 | easyrsa_req_city: "Kyiv" # The city of the organization. 20 | easyrsa_req_org: "SweetHome" # The name of the organization. 21 | easyrsa_req_email: "sweet@home.net" # The email address of the organization. 22 | easyrsa_req_ou: "MyOrganizationalUnit" # The name of the organizational unit. 23 | easyrsa_req_cn: "server" # The name of the common name. 24 | easyrsa_key_size: 2048 # Leave this as-is. Size in bits for your keypairs. The recommended value is 2048. up to 4096. 25 | easyrsa_ca_expire: 3650 # Number of days until the root CA expires. 26 | easyrsa_cert_expire: 825 # Number of days until certificates expire. 27 | easyrsa_cert_renew: 30 # Number of days before expiration to automatically renew certificates. 28 | easyrsa_crl_days: 180 # Number of days until the CRL expires. 29 | 30 | # OpenVPN monitoring configuration. # Requires `monitoring_enable` 31 | monitoring_enable: false # Set true to enable OpenVPN Server monitoring 32 | monitoring_grafana_admin_password: "admin" # Is only used the first time when Grafana starts up 33 | monitoring_days_keep_interval: 90d # How long to keep data in Prometheus DB (decrease if you have less than 5Gb of free disk space) -------------------------------------------------------------------------------- /example.config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Location where configuration files will be stored. 3 | config_dir: '~' 4 | 5 | # OpenVPN configuration. 6 | ovpn_enable: true 7 | 8 | ovpnui_user: "aws-admin" 9 | ovpnui_password: "gagaZush" # !Change this password! 10 | ovpn_trusted_subnet: "10.0.70.0/24" # Trusted users subnet 11 | ovpn_guest_subnet: "10.0.71.0/24" # Guest users subnet 12 | ovpn_home_subnet: "192.168.88.0/24" # Your home network subnet 13 | ovpn_remote: "remote 123.124.125.126 1194 udp" # OpenVPN client.ovpn profile connect line. You can set or change this via OpenVPN-UI. 14 | 15 | # EasyRSA configuration parameters. 16 | easyrsa_dn: "org" # Leave this as-is. "org" for traditional, "cn_only" for CN only. 17 | easyrsa_req_country: "UA" # The two-letter country code (e.g. US). 18 | easyrsa_req_province: "KY" # The two-letter state or province code (e.g. CA). 19 | easyrsa_req_city: "Kyiv" # The city of the organization. 20 | easyrsa_req_org: "SweetHome" # The name of the organization. 21 | easyrsa_req_email: "sweet@home.net" # The email address of the organization. 22 | easyrsa_req_ou: "MyOrganizationalUnit" # The name of the organizational unit. 23 | easyrsa_req_cn: "server" # The name of the common name. 24 | easyrsa_key_size: 2048 # Leave this as-is. Size in bits for your keypairs. The recommended value is 2048. up to 4096. 25 | easyrsa_ca_expire: 3650 # Number of days until the root CA expires. 26 | easyrsa_cert_expire: 825 # Number of days until certificates expire. 27 | easyrsa_cert_renew: 30 # Number of days before expiration to automatically renew certificates. 28 | easyrsa_crl_days: 180 # Number of days until the CRL expires. 29 | 30 | # OpenVPN monitoring configuration. # Requires `monitoring_enable` 31 | monitoring_enable: false # Set true to enable OpenVPN Server monitoring 32 | monitoring_grafana_admin_password: "admin" # Is only used the first time when Grafana starts up 33 | monitoring_days_keep_interval: 90d # How long to keep data in Prometheus DB (decrease if you have less than 5Gb of free disk space) -------------------------------------------------------------------------------- /openvpn-server/openvpn-docker/bin/rmcert.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #VERSION 0.3 by d3vilh@github.com aka Mr. Philipp 3 | # Exit immediately if a command exits with a non-zero status 4 | set -e 5 | 6 | #Variables 7 | CERT_NAME=$1 8 | CERT_SERIAL=$2 9 | EASY_RSA=/usr/share/easy-rsa 10 | OPENVPN_DIR=/etc/openvpn 11 | echo 'EasyRSA path: $EASY_RSA OVPN path: $OPENVPN_DIR' 12 | OVPN_FILE_PATH="$OPENVPN_DIR/clients/$CERT_NAME.ovpn" 13 | QR_CODE_PATH="$OPENVPN_DIR/clients/$CERT_NAME.png" # 2FA QR code 14 | OATH_SECRETS="$OPENVPN_DIR/clients/oath.secrets" # 2FA secrets file 15 | INDEX="$EASY_RSA/pki/index.txt" 16 | 17 | echo "Removing user: $CERT_NAME with Serial: $CERT_SERIAL" 18 | 19 | # Define if cert is valid or revoked 20 | STATUS_CH=$(grep -e ${CERT_NAME}$ -e${CERT_NAME}/ ${INDEX} | awk '{print $1}' | tr -d '\n') 21 | if [[ $STATUS_CH = "V" ]]; then 22 | echo "Cert is VALID\nShould not remove: $CERT_NAME with serial: $CERT_SERIAL\nExiting..." 23 | exit 1 24 | else 25 | echo "Cert is REVOKED\nContinue to remove: $CERT_NAME with serial: $CERT_SERIAL" 26 | fi 27 | 28 | # Check if the user has two certificates in index.txt 29 | if [[ $(cat $INDEX | grep -c "/CN=$CERT_NAME/") -eq 2 ]]; then 30 | echo "Removing renewed certificate..." 31 | sed -i'.bak' "/${CERT_SERIAL}/d" $INDEX 32 | # removing *.ovpn file because it has old certificate 33 | rm -f $OVPN_FILE_PATH 34 | 35 | echo 'Generating New .ovpn file...' 36 | CA="$(cat $EASY_RSA/pki/ca.crt )" 37 | CERT="$(cat $EASY_RSA/pki/issued/${CERT_NAME}.crt | grep -zEo -e '-----BEGIN CERTIFICATE-----(\n|.)*-----END CERTIFICATE-----' | tr -d '\0')" 38 | KEY="$(cat $EASY_RSA/pki/private/${CERT_NAME}.key)" 39 | TLS_AUTH="$(cat $EASY_RSA/pki/ta.key)" 40 | echo "$(cat $OPENVPN_DIR/config/client.conf) 41 | 42 | $CA 43 | 44 | 45 | $CERT 46 | 47 | 48 | $KEY 49 | 50 | 51 | $TLS_AUTH 52 | 53 | " > "$OVPN_FILE_PATH" 54 | echo "New .ovpn file created." 55 | 56 | else 57 | echo "Removing certificate...\nRemoving *.ovpn file" 58 | rm -f $OVPN_FILE_PATH 59 | rm -f $QR_CODE_PATH 60 | # Remove user from oath.secrets 61 | sed -i'.bak' "/${TFA_NAME}/d" $OATH_SECRETS 62 | # Removing the user from the list following the serial number 63 | sed -i'.bak' "/${CERT_SERIAL}/d" $INDEX 64 | echo "Database fixed." 65 | fi 66 | 67 | echo 'Remove done!\nIf you want to disconnect the user please restart the OpenVPN service or container.' 68 | -------------------------------------------------------------------------------- /templates/openvpn-docker-entrypoint.sh.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #VERSION 0.2 by @d3vilh@github.com aka Mr. Philipp 3 | set -e 4 | 5 | #Variables 6 | EASY_RSA=/usr/share/easy-rsa 7 | OPENVPN_DIR=/etc/openvpn 8 | echo "EasyRSA path: $EASY_RSA OVPN path: $OPENVPN_DIR" 9 | 10 | if [[ ! -f $OPENVPN_DIR/pki/ca.crt ]]; then 11 | export EASYRSA_BATCH=1 # see https://superuser.com/questions/1331293/easy-rsa-v3-execute-build-ca-and-gen-req-silently 12 | cd $EASY_RSA 13 | 14 | # Building the CA 15 | echo 'Setting up public key infrastructure...' 16 | $EASY_RSA/easyrsa init-pki 17 | 18 | # Copy easy-rsa variables 19 | cp $OPENVPN_DIR/config/easy-rsa.vars $EASY_RSA/pki/vars 20 | 21 | # Listing env parameters: 22 | echo "Following EASYRSA variables will be used:" 23 | cat $EASY_RSA/pki/vars | awk '{$1=""; print $0}'; 24 | 25 | echo 'Generating ertificate authority...' 26 | $EASY_RSA/easyrsa build-ca nopass 27 | 28 | # Creating the Server Certificate, Key, and Encryption Files 29 | echo 'Creating the Server Certificate...' 30 | $EASY_RSA/easyrsa gen-req server nopass 31 | 32 | echo 'Sign request...' 33 | $EASY_RSA/easyrsa sign-req server server 34 | 35 | echo 'Generate Diffie-Hellman key...' 36 | $EASY_RSA/easyrsa gen-dh 37 | 38 | echo 'Generate HMAC signature...' 39 | openvpn --genkey --secret $EASY_RSA/pki/ta.key 40 | 41 | echo 'Create certificate revocation list (CRL)...' 42 | $EASY_RSA/easyrsa gen-crl 43 | chmod +r $EASY_RSA/pki/crl.pem 44 | 45 | # Copy to mounted volume 46 | cp -r $EASY_RSA/pki/. $OPENVPN_DIR/pki 47 | else 48 | 49 | echo 'PKI already set up.' 50 | fi 51 | 52 | # Listing env parameters: 53 | echo "Following EASYRSA variables were set during CA init:" 54 | cat $OPENVPN_DIR/pki/vars | awk '{$1=""; print $0}'; 55 | 56 | # Configure network 57 | mkdir -p /dev/net 58 | if [ ! -c /dev/net/tun ]; then 59 | mknod /dev/net/tun c 10 200 60 | fi 61 | 62 | echo 'Configuring networking rules...' 63 | if ! grep -q 'net.ipv4.ip_forward=1' /etc/sysctl.conf; then 64 | echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf; 65 | echo 'IP forwarding configuration now applied:' 66 | else 67 | echo 'IP forwarding configuration already applied:' 68 | fi 69 | sysctl -p /etc/sysctl.conf 70 | 71 | echo 'Configuring iptables...' 72 | echo 'NAT for OpenVPN clients' 73 | iptables -t nat -A POSTROUTING -s $TRUST_SUB -o eth0 -j MASQUERADE 74 | iptables -t nat -A POSTROUTING -s $GUEST_SUB -o eth0 -j MASQUERADE 75 | 76 | echo 'Blocking ICMP for external clients' 77 | iptables -A FORWARD -p icmp -j DROP --icmp-type echo-request -s $GUEST_SUB 78 | iptables -A FORWARD -p icmp -j DROP --icmp-type echo-reply -s $GUEST_SUB 79 | 80 | echo 'Blocking internal home subnet to access from external openvpn clients (Internet still available)' 81 | iptables -A FORWARD -s $GUEST_SUB -d $HOME_SUB -j DROP 82 | 83 | if [[ ! -s fw-rules.sh ]]; then 84 | echo "No additional firewall rules to apply." 85 | else 86 | echo "Applying firewall rules" 87 | ./fw-rules.sh 88 | echo 'Additional firewall rules applied.' 89 | fi 90 | 91 | echo 'IPT MASQ Chains:' 92 | iptables -t nat -L | grep MASQ 93 | echo 'IPT FWD Chains:' 94 | iptables -v -x -n -L | grep DROP 95 | 96 | echo 'Start openvpn process...' 97 | /usr/sbin/openvpn --cd $OPENVPN_DIR --script-security 2 --config $OPENVPN_DIR/server.conf -------------------------------------------------------------------------------- /openvpn-server/backup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #VERSION 1.2 by @d3vilh@github.com aka Mr. Philipp 3 | # Exit immediately if a command exits with a non-zero status 4 | set -e 5 | 6 | if [[ -z $1 || -z $2 || -z $3 ]]; then 7 | echo -e "\n\033[1mScript for Backing up or Restoration of OpenVPN Server Environment\033[0m" 8 | echo -e ' Script usage: \n\n \033[1;32mBackup usage:\033[0m sudo ./backup.sh -b "OpenVPN Server env" "Backup directory"\n \033[1;32mBackup example:\033[0m sudo ./backup.sh -b ~/openvpn-server backup/openvpn-server-030923\n\n \033[1;34mRestore usage:\033[0m sudo ./backup.sh -r "OpenVPN Server env" "Backup directory"\n \033[1;34mRestore example:\033[0m sudo ./backup.sh -r ~/openvpn-server backup/openvpn-server-030923\n' 9 | exit 1 10 | fi 11 | 12 | ACTION=$1 13 | SERVER_ENV=$2 14 | BACKUP_DIR=$3 15 | 16 | if [[ $ACTION == "-b" ]]; then 17 | # Prompt to confirm restore action 18 | read -p "Are we going to backup enviroment from \"$SERVER_ENV\" to \"$BACKUP_DIR\"? (y/n) " -n 1 -r 19 | echo 20 | if [[ $REPLY =~ ^[Yy]$ ]]; then 21 | # Perform backup action 22 | echo -e "\033[1;32mPerforming backup\033[0m" 23 | echo -e " Backup OpenVPN Server Environment from \"$SERVER_ENV\" to \"$BACKUP_DIR\"" 24 | mkdir -p $BACKUP_DIR 25 | 26 | # Backup files 27 | cp -Rp $SERVER_ENV/config $BACKUP_DIR 28 | echo " OpenVPN config backed up" 29 | cp -Rp $SERVER_ENV/db $BACKUP_DIR 30 | if [ ! -f "$BACKUP_DIR/db/data.db" ]; then 31 | echo " You pronbably have old version of OpenVPN-UI, backing up your DB with docker cp" 32 | mkdir -p $BACKUP_DIR/db; mkdir -p $SERVER_ENV/db; 33 | sudo docker cp openvpn-ui:/opt/openvpn-gui/data.db $BACKUP_DIR/db/data.db 34 | sudo cp -p $BACKUP_DIR/db/data.db $SERVER_ENV/db/data.db 35 | fi 36 | echo " OpenVPN-UI db backed up" 37 | cp -Rp $SERVER_ENV/pki $BACKUP_DIR 38 | echo " OpenVPN pki backed up" 39 | cp -Rp $SERVER_ENV/staticclients $BACKUP_DIR 40 | echo " OpenVPN staticclients backed up" 41 | cp -Rp $SERVER_ENV/clients $BACKUP_DIR 42 | echo " OpenVPN clients backed up" 43 | cp -Rp $SERVER_ENV/fw-rules.sh $BACKUP_DIR/fw-rules.sh 44 | echo " OpenVPN fw-rules.sh backed up" 45 | cp -Rp $SERVER_ENV/docker-compose.yml $BACKUP_DIR/docker-compose.yml 46 | echo " OpenVPN docker-compose.yml backed up" 47 | 48 | echo -e "\033[1;32mBackup created at $BACKUP_DIR\033[0m" 49 | else 50 | echo -e "\033[1;31mBackup creation cancelled!\033[0m" 51 | exit 1 52 | fi 53 | elif [[ $ACTION == "-r" ]]; then 54 | # Prompt to confirm restore action 55 | read -p "Are you sure you want to delete enviroment in \"$SERVER_ENV\" and restore from \"$BACKUP_DIR\"? (y/n) " -n 1 -r 56 | echo 57 | if [[ $REPLY =~ ^[Yy]$ ]]; then 58 | # Perform restore action 59 | echo -e "\033[1;34mPerforming Restore \033[0m" 60 | rm -rf $SERVER_ENV/config; cp -Rp $BACKUP_DIR/config $SERVER_ENV 61 | echo " OpenVPN config restored" 62 | rm -rf $SERVER_ENV/db; cp -Rp $BACKUP_DIR/db $SERVER_ENV 63 | echo " OpenVPN-UI db restored" 64 | rm -rf $SERVER_ENV/pki; cp -Rp $BACKUP_DIR/pki $SERVER_ENV 65 | echo " OpenVPN pki restored" 66 | rm -rf $SERVER_ENV/staticclients; cp -Rp $BACKUP_DIR/staticclients $SERVER_ENV 67 | echo " OpenVPN staticclients restored" 68 | rm -rf $SERVER_ENV/clients; cp -Rp $BACKUP_DIR/clients $SERVER_ENV 69 | echo " OpenVPN clients restored" 70 | rm -rf $SERVER_ENV/fw-rules.sh; cp -Rp $BACKUP_DIR/fw-rules.sh $SERVER_ENV/fw-rules.sh 71 | echo " OpenVPN fw-rules.sh restored" 72 | rm -rf $SERVER_ENV/docker-compose.yml; cp -Rp $BACKUP_DIR/docker-compose.yml $SERVER_ENV/docker-compose.yml 73 | echo " OpenVPN docker-compose.yml restored" 74 | echo -e "\033[1;34mRestore Completed!\033[0m" 75 | else 76 | echo -e "\033[1;31mRestore cancelled!\033[0m" 77 | exit 1 78 | fi 79 | else 80 | # Invalid action 81 | echo -e "\033[1;31mInvalid option: $ACTION\033[0m" 82 | exit 1 83 | fi 84 | -------------------------------------------------------------------------------- /openvpn-server/openvpn-docker/bin/revoke.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #VERSION 0.3 by d3vilh@github.com aka Mr. Philipp 3 | # Exit immediately if a command exits with a non-zero status 4 | set -e 5 | 6 | #Variables 7 | CERT_NAME=$1 8 | CERT_SERIAL=$2 9 | EASY_RSA=/usr/share/easy-rsa 10 | OPENVPN_DIR=/etc/openvpn 11 | echo 'EasyRSA path: $EASY_RSA OVPN path: $OPENVPN_DIR' 12 | INDEX=$EASY_RSA/pki/index.txt 13 | PERSHIY=`cat $INDEX | grep "/CN=$CERT_NAME/" | head -1 | awk '{ print $3}'` 14 | OVPN_FILE_PATH="$OPENVPN_DIR/clients/$CERT_NAME.ovpn" 15 | 16 | export EASYRSA_BATCH=1 # see https://superuser.com/questions/1331293/easy-rsa-v3-execute-build-ca-and-gen-req-silently 17 | 18 | # Check if the user has two certificates in index.txt 19 | if [[ $(cat $INDEX | grep -c "/CN=$CERT_NAME/") -eq 2 ]]; then 20 | # Check if first serial is the same as requested to revoke and if yes - revoke new cert and old cert 21 | if [[ $PERSHIY = $CERT_SERIAL ]]; then 22 | echo "Revoking renewed certificate..." 23 | 24 | # removing the end of the line starting from /name=$NAME for the line that matches the $serial pattern 25 | sed -i'.bak' "/$CERT_SERIAL/s/\/name=$CERT_NAME.*//" $INDEX 26 | echo "index.txt patched" 27 | cd $EASY_RSA 28 | 29 | #moving new cert to old dir 30 | echo "Runing: easyrsa revoke-renewed $CERT_NAME" 31 | # Revoke renewed certificate 32 | ./easyrsa revoke-renewed "$CERT_NAME" 33 | echo -e "Old certificate revoked! \nRemoving old cert from the DB" 34 | 35 | # Removing old cert from the DB 36 | sed -i'.bak' "/${CERT_SERIAL}/d" $INDEX 37 | echo "Old cert with serial $CERT_SERIAL removed from the DB" 38 | 39 | # removing *.ovpn file because it has old certificate 40 | echo "removing *.ovpn file" 41 | rm -f $OVPN_FILE_PATH 42 | 43 | echo 'Generate New .ovpn file...' 44 | CA="$(cat $EASY_RSA/pki/ca.crt )" 45 | CERT="$(cat $EASY_RSA/pki/issued/${CERT_NAME}.crt | grep -zEo -e '-----BEGIN CERTIFICATE-----(\n|.)*-----END CERTIFICATE-----' | tr -d '\0')" 46 | KEY="$(cat $EASY_RSA/pki/private/${CERT_NAME}.key)" 47 | TLS_AUTH="$(cat $EASY_RSA/pki/ta.key)" 48 | echo "$(cat $OPENVPN_DIR/config/client.conf) 49 | 50 | $CA 51 | 52 | 53 | $CERT 54 | 55 | 56 | $KEY 57 | 58 | 59 | $TLS_AUTH 60 | 61 | " > "$OVPN_FILE_PATH" 62 | echo -e "Old Certificate revoked!\nCreate new Create certificate revocation list (CRL)..." 63 | ./easyrsa gen-crl 64 | chmod +r $EASY_RSA/pki/crl.pem 65 | else 66 | cd $EASY_RSA 67 | # Fix index.txt by removing the user from the list following the serial number 68 | echo "Removing New Certificate..." 69 | mv $EASY_RSA/pki/renewed/issued/$CERT_NAME.crt $EASY_RSA/pki/issued/$CERT_NAME.crt 70 | rm -f $EASY_RSA/pki/inline/$CERT_NAME.inline 71 | # Removing old cert from the DB 72 | sed -i'.bak' "/${CERT_SERIAL}/d" $INDEX 73 | # Create new Create certificate revocation list (CRL) 74 | echo -e "New Certificate revoked!\nCreate new certificate revocation list (CRL)..." 75 | ./easyrsa gen-crl 76 | chmod +r $EASY_RSA/pki/crl.pem 77 | fi 78 | else 79 | echo "Revoking certificate..." 80 | # removing the end of the line starting from /name=$NAME for the line that matches the $serial pattern 81 | sed -i'.bak' "/$CERT_SERIAL/s/\/name=$CERT_NAME.*//" $INDEX 82 | cd $EASY_RSA 83 | # Revoke certificate 84 | ./easyrsa revoke "$CERT_NAME" 85 | 86 | echo 'Create new Create certificate revocation list (CRL)...' 87 | ./easyrsa gen-crl 88 | chmod +r $EASY_RSA/pki/crl.pem 89 | # restoring the index.txt, new /name in index.txt (adding name and ip to the last line) 90 | #sed -i'.bak' "$ s/$/\/name=${CERT_NAME}\/LocalIP=${CERT_IP}\/2FAName=${TFA_NAME}/" $EASY_RSA/pki/index.txt 91 | # adding name, ip and 2fa-name to the same CERT serial 92 | sed -i'.bak' "/${CERT_SERIAL}/ s/$/\/name=${CERT_NAME}\/LocalIP=${CERT_IP}\/2FAName=${TFA_NAME}/" $EASY_RSA/pki/index.txt 93 | fi 94 | 95 | echo -e 'Done!\nIf you want to disconnect the user please restart the service using docker-compose restart openvpn.' 96 | -------------------------------------------------------------------------------- /openvpn-server/openvpn-docker/bin/genclient.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #VERSION 0.3 by d3vilh@github.com aka Mr. Philipp 3 | # Exit immediately if a command exits with a non-zero status. 4 | set -e 5 | 6 | # .ovpn file path 7 | CERT_NAME=$1 8 | CERT_IP=$2 9 | CERT_PASS=$3 10 | # These VARS shoud be in your ENV before running certgen: TFA_NAME, ISSUER, EASYRSA_CERT_EXPIRE, EASYRSA_REQ_EMAIL, EASYRSA_REQ_COUNTRY, EASYRSA_REQ_PROVINCE, EASYRSA_REQ_CITY, EASYRSA_REQ_ORG, EASYRSA_REQ_OU 11 | 12 | EASY_RSA=/usr/share/easy-rsa 13 | OPENVPN_DIR=/etc/openvpn 14 | echo "EasyRSA path: $EASY_RSA OVPN path: $OPENVPN_DIR" 15 | OVPN_FILE_PATH="$OPENVPN_DIR/clients/$CERT_NAME.ovpn" 16 | OATH_SECRETS="$OPENVPN_DIR/clients/oath.secrets" # 2FA secrets file 17 | 18 | # Validate username and check for duplicates 19 | if [[ -z $CERT_NAME ]]; then 20 | echo 'Name cannot be empty. Exiting...' 21 | exit 1 22 | elif [[ -f $OVPN_FILE_PATH ]]; then 23 | echo "User with name $CERT_NAME already exists under openvpn/clients. Exiting..." 24 | exit 1 25 | fi 26 | 27 | export EASYRSA_BATCH=1 # see https://superuser.com/questions/1331293/easy-rsa-v3-execute-build-ca-and-gen-req-silently 28 | 29 | echo 'Patching easy-rsa.3.1.1 openssl-easyrsa.cnf...' 30 | sed -i '/serialNumber_default/d' "$EASY_RSA/pki/openssl-easyrsa.cnf" 31 | 32 | echo 'Generate client certificate...' 33 | echo -e "Will use following parameters: \nEASYRSA_CERT_EXPIRE: $EASYRSA_CERT_EXPIRE\nEASYRSA_REQ_EMAIL: $EASYRSA_REQ_EMAIL\nEASYRSA_REQ_COUNTRY: $EASYRSA_REQ_COUNTRY\nEASYRSA_REQ_PROVINCE: $EASYRSA_REQ_PROVINCE\nEASYRSA_REQ_CITY: $EASYRSA_REQ_CITY\nEASYRSA_REQ_ORG: $EASYRSA_REQ_ORG\nEASYRSA_REQ_OU: $EASYRSA_REQ_OU" 34 | 35 | # Copy easy-rsa variables 36 | cd $EASY_RSA 37 | 38 | # Generate certificates 39 | if [[ -z $CERT_PASS ]]; then 40 | echo 'Without password...' 41 | ./easyrsa --batch --req-cn="$CERT_NAME" --days="$EASYRSA_CERT_EXPIRE" --req-email="$EASYRSA_REQ_EMAIL" gen-req "$CERT_NAME" nopass subject="/C=$EASYRSA_REQ_COUNTRY/ST=$EASYRSA_REQ_PROVINCE/L=$EASYRSA_REQ_CITY/O=$EASYRSA_REQ_ORG/OU=$EASYRSA_REQ_OU" 42 | else 43 | echo 'With password...' 44 | # See https://stackoverflow.com/questions/4294689/how-to-generate-an-openssl-key-using-a-passphrase-from-the-command-line 45 | # ... and https://stackoverflow.com/questions/22415601/using-easy-rsa-how-to-automate-client-server-creation-process 46 | # ... and https://github.com/OpenVPN/easy-rsa/blob/master/doc/EasyRSA-Advanced.md 47 | (echo -e '\n') | ./easyrsa --batch --req-cn="$CERT_NAME" --days="$EASYRSA_CERT_EXPIRE" --req-email="$EASYRSA_REQ_EMAIL" --passin=pass:"${CERT_PASS}" --passout=pass:"${CERT_PASS}" gen-req "$CERT_NAME" subject="/C=$EASYRSA_REQ_COUNTRY/ST=$EASYRSA_REQ_PROVINCE/L=$EASYRSA_REQ_CITY/O=$EASYRSA_REQ_ORG/OU=$EASYRSA_REQ_OU" 48 | fi 49 | 50 | # Sign request 51 | ./easyrsa sign-req client "$CERT_NAME" 52 | # Fix for /name in index.txt 53 | 54 | # Check if 2FA was specified. If not - set to none. 55 | if [ -z "$TFA_NAME" ]; then 56 | TFA_NAME="none" 57 | fi 58 | 59 | echo "Fixing Database..." 60 | sed -i'.bak' "$ s/$/\/name=${CERT_NAME}\/LocalIP=${CERT_IP}\/2FAName=${TFA_NAME}/" $EASY_RSA/pki/index.txt 61 | echo "Database fixed:" 62 | tail -1 $EASY_RSA/pki/index.txt 63 | # Certificate properties 64 | CA="$(cat $EASY_RSA/pki/ca.crt )" 65 | CERT="$(awk '/-----BEGIN CERTIFICATE-----/{flag=1;next}/-----END CERTIFICATE-----/{flag=0}flag' ./pki/issued/${CERT_NAME}.crt | tr -d '\0')" 66 | KEY="$(cat $EASY_RSA/pki/private/${CERT_NAME}.key)" 67 | TLS_AUTH="$(cat $EASY_RSA/pki/ta.key)" 68 | 69 | echo 'Fixing permissions for pki/issued...' 70 | chmod +r $EASY_RSA/pki/issued 71 | 72 | echo 'Generating .ovpn file...' 73 | echo "$(cat $OPENVPN_DIR/config/client.conf) 74 | 75 | $CA 76 | 77 | 78 | $CERT 79 | 80 | 81 | $KEY 82 | 83 | 84 | $TLS_AUTH 85 | 86 | " > "$OVPN_FILE_PATH" 87 | 88 | echo -e "OpenVPN Client configuration successfully generated!\nCheckout openvpn-server/clients/$CERT_NAME.ovpn" 89 | 90 | # Check if $TFA_NAME was specified and not equal to "none". then create 2FA and QR code 91 | if [[ ! -z $TFA_NAME ]] && [[ $TFA_NAME != "none" ]]; then 92 | echo -e "Generating 2FA ...\nName: $TFA_NAME\nIssuer: $TFA_ISSUER" 93 | 94 | # Userhash. Random 30 chars 95 | USERHASH=$(head -c 10 /dev/urandom | openssl sha256 | cut -d ' ' -f2 | cut -b 1-30) 96 | 97 | # Base32 secret from oathtool output 98 | BASE32=$(oathtool --totp -v "$USERHASH" | grep Base32 | awk '{print $3}') 99 | 100 | # QRCODE STRING 101 | QRSTRING="otpauth://totp/$TFA_ISSUER:$TFA_NAME?secret=$BASE32" 102 | 103 | # QR code for user to pass to Google Authenticator or OpenVPN-UI 104 | echo "User String for QR:" 105 | echo $QRSTRING 106 | 107 | /opt/scripts/qrencode "$QRSTRING" > $OPENVPN_DIR/clients/$CERT_NAME.png 108 | 109 | # New string for secrets file 110 | echo "oath.secrets entry for BackEnd:" 111 | echo "$TFA_NAME:$USERHASH" | tee -a $OATH_SECRETS 112 | 113 | else 114 | echo 'No 2FA specified. exiting' 115 | 116 | fi -------------------------------------------------------------------------------- /openvpn-server/openvpn-docker/openssl-easyrsa.cnf: -------------------------------------------------------------------------------- 1 | # For use with Easy-RSA 3.0+ and OpenSSL or LibreSSL 2 | 3 | #################################################################### 4 | [ ca ] 5 | default_ca = CA_default # The default ca section 6 | 7 | #################################################################### 8 | [ CA_default ] 9 | 10 | dir = $ENV::EASYRSA_PKI # Where everything is kept 11 | certs = $dir # Where the issued certs are kept 12 | crl_dir = $dir # Where the issued crl are kept 13 | database = $dir/index.txt # database index file. 14 | new_certs_dir = $dir/certs_by_serial # default place for new certs. 15 | 16 | certificate = $dir/ca.crt # The CA certificate 17 | serial = $dir/serial # The current serial number 18 | crl = $dir/crl.pem # The current CRL 19 | private_key = $dir/private/ca.key # The private key 20 | RANDFILE = $dir/.rand # private random number file 21 | 22 | x509_extensions = basic_exts # The extensions to add to the cert 23 | 24 | # This allows a V2 CRL. Ancient browsers don't like it, but anything Easy-RSA 25 | # is designed for will. In return, we get the Issuer attached to CRLs. 26 | crl_extensions = crl_ext 27 | 28 | default_days = $ENV::EASYRSA_CERT_EXPIRE # how long to certify for 29 | default_crl_days= $ENV::EASYRSA_CRL_DAYS # how long before next CRL 30 | default_md = $ENV::EASYRSA_DIGEST # use public key default MD 31 | preserve = no # keep passed DN ordering 32 | 33 | # This allows to renew certificates which have not been revoked 34 | unique_subject = no 35 | 36 | # A few different ways of specifying how similar the request should look 37 | # For type CA, the listed attributes must be the same, and the optional 38 | # and supplied fields are just that :-) 39 | policy = policy_anything 40 | 41 | # For the 'anything' policy, which defines allowed DN fields 42 | [ policy_anything ] 43 | countryName = optional 44 | stateOrProvinceName = optional 45 | localityName = optional 46 | organizationName = optional 47 | organizationalUnitName = optional 48 | commonName = supplied 49 | name = optional 50 | emailAddress = optional 51 | 52 | #################################################################### 53 | # Easy-RSA request handling 54 | # We key off $DN_MODE to determine how to format the DN 55 | [ req ] 56 | default_bits = $ENV::EASYRSA_KEY_SIZE 57 | default_keyfile = privkey.pem 58 | default_md = $ENV::EASYRSA_DIGEST 59 | distinguished_name = $ENV::EASYRSA_DN 60 | x509_extensions = easyrsa_ca # The extensions to add to the self signed cert 61 | 62 | # A placeholder to handle the $EXTRA_EXTS feature: 63 | #%EXTRA_EXTS% # Do NOT remove or change this line as $EXTRA_EXTS support requires it 64 | 65 | #################################################################### 66 | # Easy-RSA DN (Subject) handling 67 | 68 | # Easy-RSA DN for cn_only support: 69 | [ cn_only ] 70 | commonName = Common Name (eg: your user, host, or server name) 71 | commonName_max = 64 72 | commonName_default = $ENV::EASYRSA_REQ_CN 73 | 74 | # Easy-RSA DN for org support: 75 | [ org ] 76 | countryName = Country Name (2 letter code) 77 | countryName_default = $ENV::EASYRSA_REQ_COUNTRY 78 | countryName_min = 2 79 | countryName_max = 2 80 | 81 | stateOrProvinceName = State or Province Name (full name) 82 | stateOrProvinceName_default = $ENV::EASYRSA_REQ_PROVINCE 83 | 84 | localityName = Locality Name (eg, city) 85 | localityName_default = $ENV::EASYRSA_REQ_CITY 86 | 87 | 0.organizationName = Organization Name (eg, company) 88 | 0.organizationName_default = $ENV::EASYRSA_REQ_ORG 89 | 90 | organizationalUnitName = Organizational Unit Name (eg, section) 91 | organizationalUnitName_default = $ENV::EASYRSA_REQ_OU 92 | 93 | commonName = Common Name (eg: your user, host, or server name) 94 | commonName_max = 64 95 | commonName_default = $ENV::EASYRSA_REQ_CN 96 | 97 | emailAddress = Email Address 98 | emailAddress_default = $ENV::EASYRSA_REQ_EMAIL 99 | emailAddress_max = 64 100 | 101 | #################################################################### 102 | # Easy-RSA cert extension handling 103 | 104 | # This section is effectively unused as the main script sets extensions 105 | # dynamically. This core section is left to support the odd usecase where 106 | # a user calls openssl directly. 107 | [ basic_exts ] 108 | basicConstraints = CA:FALSE 109 | subjectKeyIdentifier = hash 110 | authorityKeyIdentifier = keyid,issuer:always 111 | 112 | # The Easy-RSA CA extensions 113 | [ easyrsa_ca ] 114 | 115 | # PKIX recommendations: 116 | 117 | subjectKeyIdentifier=hash 118 | authorityKeyIdentifier=keyid:always,issuer:always 119 | 120 | # This could be marked critical, but it's nice to support reading by any 121 | # broken clients who attempt to do so. 122 | basicConstraints = CA:true 123 | 124 | # Limit key usage to CA tasks. If you really want to use the generated pair as 125 | # a self-signed cert, comment this out. 126 | keyUsage = cRLSign, keyCertSign 127 | 128 | # nsCertType omitted by default. Let's try to let the deprecated stuff die. 129 | # nsCertType = sslCA 130 | 131 | # CRL extensions. 132 | [ crl_ext ] 133 | 134 | # Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. 135 | 136 | # issuerAltName=issuer:copy 137 | authorityKeyIdentifier=keyid:always,issuer:always 138 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /openvpn-server/openvpn-docker/README.md: -------------------------------------------------------------------------------- 1 | # OpenVPN and OpenVPN WEB UI 2 | 3 | Simple OpenVPN instance for EC2 T2-micro based AWS server. 4 | It does include 2 different Docker containers: 5 | - OpenVPN Back-End container (openvpn) powered by Alpine linux and 6 | - OpenVPN WEB UI Front-End container (openvpn-ui) for managing OpenVPN server. 7 | 8 | ### Run this image using a `docker-compose.yml` file 9 | 10 | ```yaml 11 | --- 12 | version: "3.5" 13 | 14 | services: 15 | openvpn: 16 | container_name: openvpn 17 | build: ./openvpn-docker 18 | privileged: true 19 | ports: 20 | - "1194:1194/udp" 21 | environment: 22 | REQ_COUNTRY: UA 23 | REQ_PROVINCE: Kyiv 24 | REQ_CITY: Chayka 25 | REQ_ORG: CopyleftCertificateCo 26 | REQ_OU: ShantiShanti 27 | REQ_CN: MyOpenVPN 28 | volumes: 29 | - ./pki:/etc/openvpn/pki 30 | - ./clients:/etc/openvpn/clients 31 | - ./config:/etc/openvpn/config 32 | - ./staticclients:/etc/openvpn/staticclients 33 | - ./log:/var/log/openvpn 34 | cap_add: 35 | - NET_ADMIN 36 | restart: always 37 | depends_on: 38 | - "openvpn-ui" 39 | ``` 40 | 41 | Here is how the `Dockerfile` looks like: 42 | 43 | ```yaml 44 | # Start from Alpine base image 45 | FROM amd64/alpine 46 | LABEL maintainer="Mr.Philipp " 47 | 48 | # Copy all files in the current directory to the /opt/app directory in the container 49 | COPY . /opt/app 50 | # Set the working directory to /opt/app 51 | WORKDIR /opt/app 52 | 53 | RUN apk --no-cache --no-progress upgrade && apk --no-cache --no-progress add bash bind-tools curl ip6tables iptables openvpn easy-rsa 54 | #Install Latest RasyRSA Version 55 | RUN chmod 755 /usr/share/easy-rsa/* 56 | 57 | # Add the openssl-easyrsa.cnf file to the easy-rsa directory 58 | ADD openssl-easyrsa.cnf /opt/app/easy-rsa/ 59 | 60 | # Expose the OpenVPN port (1194/udp) 61 | EXPOSE 1194/udp 62 | 63 | # Make all files in the bin directory executable 64 | RUN chmod +x bin/* 65 | 66 | # Make the docker-entrypoint.sh script executable 67 | RUN chmod +x docker-entrypoint.sh 68 | 69 | # Set the entrypoint to the docker-entrypoint.sh script, passing in the following arguments: 70 | # $REQ_COUNTRY $REQ_PROVINCE $REQ_CITY $REQ_ORG $REQ_OU $REQ_CN 71 | ENTRYPOINT ./docker-entrypoint.sh \ 72 | "$REQ_COUNTRY" \ 73 | "$REQ_PROVINCE" \ 74 | "$REQ_CITY" \ 75 | "$REQ_ORG" \ 76 | "$REQ_OU" \ 77 | "$REQ_CN" 78 | ``` 79 | 80 | 81 | Alternatevly you can add OpenVPN-UI container for WEB UI: 82 | ```yaml 83 | openvpn-ui: 84 | container_name: openvpn-ui 85 | image: d3vilh/openvpn-ui-amd64:latest 86 | environment: 87 | - OPENVPN_ADMIN_USERNAME={{ ovpnui_user }} 88 | - OPENVPN_ADMIN_PASSWORD={{ ovpnui_password }} 89 | privileged: true 90 | ports: 91 | - "8080:8080/tcp" 92 | volumes: 93 | - ./:/etc/openvpn 94 | - ./db:/opt/openvpn-gui/db 95 | - ./pki:/usr/share/easy-rsa/pki 96 | restart: always 97 | ``` 98 | 99 | ### Run this image using the Docker itself 100 | 101 | First, build the images: 102 | ```sh 103 | sudo docker build -t openvpn . 104 | ``` 105 | 106 | Run the OpenVPN image: 107 | ```sh 108 | sudo docker run openvpn \ 109 | --expose 1194:1194/udp \ 110 | --mount type=bind,src=./openvpn/pki,dst=/etc/openvpn/pki \ 111 | --mount type=bind,src=./openvpn/clients,dst=/etc/openvpn/clients \ 112 | --mount type=bind,src=./openvpn/config,dst=/etc/openvpn/config \ 113 | --mount type=bind,src=./openvpn/staticclients,dst=/etc/openvpn/staticclients \ 114 | --mount type=bind,src=./openvpn/log,dst=/var/log/openvpn \ 115 | --cap-add=NET_ADMIN \ 116 | --restart=unless-stopped 117 | --privileged openvpn 118 | ``` 119 | 120 | Run the OpenVPN-UI image 121 | ``` 122 | docker run \ 123 | -v /home/pi/openvpn:/etc/openvpn \ 124 | -v /home/pi/openvpn/db:/opt/openvpn-gui/db \ 125 | -v /home/pi/openvpn/pki:/usr/share/easy-rsa/pki \ 126 | -e OPENVPN_ADMIN_USERNAME='admin' \ 127 | -e OPENVPN_ADMIN_PASSWORD='gagaZush' \ 128 | -p 8080:8080/tcp \ 129 | --privileged local/openvpn-ui 130 | ``` 131 | 132 | Most of documentation can be found in the [main README.md](https://github.com/d3vilh/raspberry-gateway) file, if you want to run it without anything else you'll have to edit the [dns-configuration](https://github.com/d3vilh/raspberry-gateway/blob/master/openvpn/config/server.conf#L20) (which currently points to the PiHole DNS Server) and 133 | if you don't want to use a custom dns-resolve at all you may also want to comment out [this line](https://github.com/d3vilh/raspberry-gateway/blob/master/openvpn/config/server.conf#L39). 134 | 135 | ## Configuration 136 | 137 | **OpenVPN WEB UI** can be accessed on own port (*e.g. http://localhost:8080 , change `localhost` to your EC2's Public or Private IPv4 address*), the default user and password is `admin/gagaZush` preconfigured in `config.yml` which you supposed to [set in](https://github.com/d3vilh/openvpn-aws/blob/master/example.config.yml#L18) `ovpnui_user` & `ovpnui_password` vars, just before the installation. 138 | 139 | The volume container will be inicialized by using the official OpenVPN `openvpn_openvpn` image with included scripts to automatically generate everything you need on the first run: 140 | - Diffie-Hellman parameters 141 | - an EasyRSA CA key and certificate 142 | - a new private key 143 | - a self-certificate matching the private key for the OpenVPN server 144 | - a TLS auth key from HMAC security 145 | 146 | This setup use `tun` mode, because it works on the widest range of devices. tap mode, for instance, does not work on Android, except if the device is rooted. 147 | 148 | The topology used is `subnet`, because it works on the widest range of OS. p2p, for instance, does not work on Windows. 149 | 150 | The server config [specifies](https://github.com/d3vilh/openvpn-aws/blob/master/openvpn/config/server.conf#L40) `push redirect-gateway def1 bypass-dhcp`, meaning that after establishing the VPN connection, all traffic will go through the VPN. This might cause problems if you use local DNS recursors which are not directly reachable, since you will try to reach them through the VPN and they might not answer to you. If that happens, use public DNS resolvers like those of OpenDNS (`208.67.222.222` and `208.67.220.220`) or Google (`8.8.4.4` and `8.8.8.8`). 151 | 152 | ### Generating .OVPN client profiles 153 | 154 | Before client cert. generation you need to update the external IP address to your OpenVPN server in OVPN-UI GUI. 155 | 156 | For this go to `"Configuration > Settings"`: 157 | 158 | Configuration > Settings 159 | 160 | And then update `"Server Address (external)"` field with your external Internet IP. Then go to `"Certificates"`, enter new VPN client name in the field at the page below and press `"Create"` to generate new Client certificate: 161 | 162 | Server Address Create Certificate 163 | 164 | To download .OVPN client configuration file, press on the `Client Name` you just created: 165 | 166 | download OVPN 167 | 168 | If you use NAT and different port for all the external connections on your network router, you may need to change server port in .OVPN file. For that, just open it in any text editor (emax?) and update `1194` port with the desired one in this line: `remote 178.248.232.12 1194 udp`. 169 | This line also can be [preconfigured in](https://github.com/d3vilh/openvpn-aws/blob/master/example.config.yml#L23) `config.yml` file in var `ovpn_remote`. 170 | 171 | Install [Official OpenVPN client](https://openvpn.net/vpn-client/) to your client device. 172 | 173 | Deliver .OVPN profile to the client device and import it as a FILE, then connect with new profile to enjoy your free VPN: 174 | 175 | PalmTX Import PalmTX Connected 176 | 177 | ### Revoking .OVPN profiles 178 | 179 | If you would like to prevent client to use yor VPN connection, you have to revoke client certificate and restart the OpenVPN daemon. 180 | You can do it via OpenVPN WEB UI `"Certificates"` menue, by pressing Revoke red button: 181 | 182 | Revoke Certificate 183 | 184 | Revoked certificates won't kill active connections, you'll have to restart the service if you want the user to immediately disconnect. It can be done via Portainer or OpenVPN WEB UI from the same `"Certificates"` page, by pressing Restart red button: 185 | 186 | OpenVPN Restart 187 | 188 | ### OpenVPN client subnets. Guest and Home users 189 | 190 | [OpenVPN-AWS'](https://github.com/d3vilh/openvpn-aws/) OpenVPN server uses `10.0.70.0/24` **"Trusted"** subnet for dynamic clients by default and all the clients connected by default will have full access to your AWS Private subnet, as well as external Internet access with EC2 Public IP. 191 | However you can be desired to share VPN access with your friends and restrict access to your AWS Private network for them (so they wont access OpenVPN-UI GUI or other services), but allow to use Internet connection with EC2 Public IP. This type of guest clients needs to live in special **"Guest users"** subnet - `10.0.71.0/24`: 192 | 193 | To assign desired subnet policy to the specific client, you have to define static IP address for this client after you generate .OVPN profile. 194 | 195 | > Keep in mind, by default, all the clients have full access, so you don't need to specifically configure static IP for your own devices, your home devices always will land to **"Trusted"** subnet by default. 196 | 197 | ### OpenVPN Pstree structure 198 | 199 | All the Server and Client configuration located in Docker volume and can be easely tuned. Here are tree of volume content: 200 | 201 | ```shell 202 | |-- clients 203 | | |-- your_client1.ovpn 204 | |-- config 205 | | |-- client.conf 206 | | |-- easy-rsa.vars 207 | | |-- server.conf 208 | |-- db 209 | | |-- data.db //OpenVPN UI DB 210 | |-- log 211 | | |-- openvpn.log 212 | |-- pki 213 | | |-- ca.crt 214 | | |-- certs_by_serial 215 | | | |-- your_client1_serial.pem 216 | | |-- crl.pem 217 | | |-- dh.pem 218 | | |-- index.txt 219 | | |-- ipp.txt 220 | | |-- issued 221 | | | |-- server.crt 222 | | | |-- your_client1.crt 223 | | |-- openssl-easyrsa.cnf 224 | | |-- private 225 | | | |-- ca.key 226 | | | |-- your_client1.key 227 | | | |-- server.key 228 | | |-- renewed 229 | | | |-- certs_by_serial 230 | | | |-- private_by_serial 231 | | | |-- reqs_by_serial 232 | | |-- reqs 233 | | | |-- server.req 234 | | | |-- your_client1.req 235 | | |-- revoked 236 | | | |-- certs_by_serial 237 | | | |-- private_by_serial 238 | | | |-- reqs_by_serial 239 | | |-- safessl-easyrsa.cnf 240 | | |-- serial 241 | | |-- ta.key 242 | |-- staticclients //Directory where stored all the satic clients configuration 243 | ``` 244 | 245 | ### Alternative, CLI ways to deal with OpenVPN configuration 246 | 247 | To generate new .OVPN profile execute following command. Password as second argument is optional: 248 | ```shell 249 | sudo docker exec openvpn bash /opt/app/bin/genclient.sh 250 | ``` 251 | 252 | You can find you .ovpn file under `/openvpn/clients/.ovpn`, make sure to check and modify the `remote ip-address`, `port` and `protocol`. It also will appear in `"Certificates"` menue of OpenVPN WEB UI. 253 | 254 | Revoking of old .OVPN files can be done via CLI by running following: 255 | 256 | ```shell 257 | sudo docker exec openvpn bash /opt/app/bin/revoke.sh 258 | ``` 259 | 260 | Removing of old .OVPN files can be done via CLI by running following: 261 | 262 | ```shell 263 | sudo docker exec openvpn bash /opt/app/bin/rmcert.sh 264 | ``` 265 | 266 | Restart of OpenVPN container can be done via the CLI by running following: 267 | ```shell 268 | sudo docker-compose restart openvpn 269 | ``` 270 | 271 | To define static IP, go to `~/openvpn/staticclients` directory and create text file with the name of your client and insert into this file ifrconfig-push option with the desired static IP and mask: `ifconfig-push 10.0.71.2 255.255.255.0`. 272 | 273 | For example, if you would like to restrict Home subnet access to your best friend Slava, you should do this: 274 | 275 | ```shell 276 | slava@Ukraini:~/openvpn/staticclients $ pwd 277 | /home/slava/openvpn/staticclients 278 | slava@Ukraini:~/openvpn/staticclients $ ls -lrt | grep Slava 279 | -rw-r--r-- 1 slava heroi 38 Nov 9 20:53 Slava 280 | slava@Ukraini:~/openvpn/staticclients $ cat Slava 281 | ifconfig-push 10.0.71.2 255.255.255.0 282 | ``` 283 | 284 | > Keep in mind, by default, all the clients have full access, so you don't need to specifically configure static IP for your own devices, your home devices always will land to **"Trusted"** subnet by default. 285 | 286 | [**OpenVPN**](https://openvpn.net) as a server and **OpenVPN-web-ui** as a WEB UI screenshots: 287 | 288 | OpenVPN-UI Login screen 289 | 290 | OpenVPN-UI Home screen 291 | 292 | OpenVPN-UI Certificates screen 293 | 294 | OpenVPN-UI Logs screen 295 | 296 | OpenVPN-UI Configuration screen 297 | 298 | OpenVPN-UI Server Configuration screen 299 | 300 | OpenVPN-UI User Profile 301 | 302 | Build 22.01.2023 by [d3vilh](https://github.com/d3vilh) for small home project. 303 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenVPN AWS 2 | 3 | **OpenVPN instance**. Which includes 4 | [**OpenVPN container**](https://github.com/d3vilh/openvpn-server) with simple [**WEB UI**](https://github.com/d3vilh/openvpn-ui) as lightweight web administration interface: 5 | 6 | Openvpn-ui home screen 7 | 8 | # Requirements 9 | **Any Intel or AMD x86 based computer**, or x86 VM, or cloud instance with at least 1 CPU core and 512Mb RAM. 10 | 11 | For Amazon AWS will be enough: 12 | - [**Amazon AWS EC2 T2 Micro Instance**](https://aws.amazon.com/ec2/instance-types/t2/) 1x CPU Core, 1Gb RAM 13 | - [**Amazon AWS Debian amd64 AMI**](https://wiki.debian.org/Cloud/AmazonEC2Image/Bullseye),Debian 11 Bullseye 14 | - **At least 4Gb GP2 Storage** 15 | - **Opened UDP/1194 and TCP/8080** ports (TCP/8080 necessary for [OpenVPN-UI](https://github.com/d3vilh/openvpn-ui) initial configuration only) 16 | > Theoretically [OpenVPN AWS](https://github.com/d3vilh/openvpn-aws) will run on EC2 T2 Nano Instance (1x CPU Core, 512Mb RAM), it was never tested (however it runs very well on ARM based Raspberry Pi Zero1 with 512MB RAM). 17 | 18 | # Installation 19 | 20 | 1. Install [Ansible](https://docs.ansible.com/ansible) and [Git](https://git-scm.com): 21 | ```shell 22 | sudo apt-get install -y python3-pip git rsync ansible 23 | ``` 24 | 2. Clone this repository: 25 | ```shell 26 | git clone https://github.com/d3vilh/openvpn-aws 27 | ``` 28 | 3. Then enter the repository directory: 29 | ```shell 30 | cd openvpn-aws 31 | ``` 32 | 4. Install requirements: 33 | ```shell 34 | ansible-galaxy collection install -r requirements.yml --force 35 | ``` 36 | > If you see `ansible-galaxy: command not found`, you have to relogin and then try again. 37 | 5. Make copies of the configuration files and modify them for your enviroment: 38 | ```shell 39 | yes | cp -p example.config.yml config.yml 40 | ``` 41 | 6. Run the following command to add the `docker` group if it doesn't exist and add user to the `docker` group: 42 | ```shell 43 | sudo groupadd docker 44 | sudo usermod -aG docker $USER 45 | ``` 46 | **RELOGIN NOW** to apply group changes. 47 | 7. **Double check** that `ansible_user` is correct for `inventory.yml`. Need to run installtion on the remote server - follow the recomendations in config file. 48 | 49 | > **Note**: To make all necesary changes: `nano inventory.yml`, save the file - `Ctrl+O` and `Ctrl+X` to exit. 50 | 51 | 8. Run installation playbook: 52 | ```shell 53 | sudo ansible-playbook main.yml 54 | ``` 55 | > **If running locally on the EC2**: You may have error like `Error while fetching server API version` or `Permission denied`. You have to relogin and then run the playbook again. 56 | 57 | 9. Now, open your [EC2 console](https://console.aws.amazon.com/ec2/home) and go to `"Network & Security"` > `"Security Groups"` to create two new Security groups for opening OpenVPN UDP/1194 and OpenVPN-UI TCP/8080 ports for your EC2 instance. 58 | 59 | 10. Configure Security Group for OpenVPN (to open UDP/1194 port for Public access): 60 | 61 | Opening EC2 Public OVPN Port 62 | 63 | 11. Configure Security Group for OpenVPN-UI (to open TCP/8080 port for Public access): 64 | 65 | Opening EC2 Public OpenVPN-UI Port 66 | 67 | 12. Assign both Security groups to your running EC2 instance. 68 | 69 | 13. **Generate first .OVPN profile** as described below, with **"Trusted"** subnet IP (`10.0.70.0/24`) via OpenVPN-UI web GUI and connect with it, to check your setup. 70 | 71 | 14. Now when you have OpenVPN-UI access over VPN you **MUST** remove OpenVPN-UI Security group profile from you EC2 instance settings. **Because of security reasons!!!** 72 | 73 | # Usage 74 | 75 | **OpenVPN WEB UI** can be accessed on own port (*e.g. http://localhost:8080 , change `localhost` to your EC2's Public or Private IPv4 address*), the default user and password is `aws-admin/gagaZush` preconfigured in `config.yml` which you supposed to [set in](https://github.com/d3vilh/openvpn-aws/blob/master/example.config.yml#L9) `ovpnui_user` & `ovpnui_password` vars, just before the installation. 76 | 77 | ### Container volume 78 | The container volume can be initialized by using the [d3vilh/openvpn-server](https://github.com/d3vilh/openvpn-server) image with included scripts to automatically generate everything you need on the first run: 79 | - Diffie-Hellman parameters 80 | - an EasyRSA CA key and certificate 81 | - a new private key 82 | - a self-certificate matching the private key for the OpenVPN server 83 | - a TLS auth key from HMAC security 84 | 85 | However you can generate all the above components on OpenVPN UI `Configuration > Maintenance` page. 86 | 87 | ### EasyRSA vars 88 | You can update all EasyRSA parameters with OpenVPN UI on `Configuration > EasyRSA vars` page. You also can set custom EasyRSA vars for every new Client Certificate during its creation. 89 | 90 | Default EasyRSA configuration [can be set prior](https://github.com/d3vilh/openvpn-aws/blob/master/example.config.yml#L9) installation in `config.yml` file: 91 | 92 | ```shell 93 | # EasyRSA configuration parameters. 94 | easyrsa_dn: "org" # Leave this as-is. "org" for traditional, "cn_only" for CN only. 95 | easyrsa_req_country: "UA" # The two-letter country code (e.g. US). 96 | easyrsa_req_province: "KY" # The two-letter state or province code (e.g. CA). 97 | easyrsa_req_city: "Kyiv" # The city of the organization. 98 | easyrsa_req_org: "SweetHome" # The name of the organization. 99 | easyrsa_req_email: "sweet@home.net" # The email address of the organization. 100 | easyrsa_req_ou: "MyOrganizationalUnit" # The name of the organizational unit. 101 | easyrsa_req_cn: "server" # The name of the common name. 102 | easyrsa_key_size: 2048 # Leave this as-is. Size in bits for your keypairs. 103 | easyrsa_ca_expire: 3650 # Number of days until the root CA expires. 104 | easyrsa_cert_expire: 825 # Number of days until certificates expire. 105 | easyrsa_cert_renew: 30 # Number of days before expiration to renew certificates. 106 | easyrsa_crl_days: 180 # Number of days until the CRL expires. 107 | 108 | ``` 109 | In the process of installation these vars will be copied to container volume `/etc/openvpn/pki/vars` and used during all EasyRSA operations. 110 | 111 | ### Monitoring 112 | To install OpenVPN Grafana Dashboard you have to set following configuration parameters in `config.yml` file: 113 | ```shell 114 | # OpenVPN monitoring configuration. # Requires `monitoring_enable` 115 | monitoring_enable: true # Set true to enable OpenVPN Server monitoring 116 | monitoring_grafana_admin_password: "admin" # Is only used the first time when Grafana starts up 117 | monitoring_days_keep_interval: 90d # How long to keep data in Prometheus DB (decrease if you have less than 5Gb of free disk space) 118 | ``` 119 | It will install Prometheus and Grafana containers and configure them to collect OpenVPN Server metrics. 120 | 121 | ### Network configuration 122 | 123 | This setup use `tun` mode by default, because it works on the widest range of devices. `tap` mode, for instance, does not work on Android, except if the device is rooted. 124 | 125 | The default topology is `subnet`, because it works on the widest range of OS. `p2p`, for instance, does not work on Windows. 126 | 127 | The server config by default [specifies](https://github.com/d3vilh/openvpn-server/tree/main/config/server.conf#L35) `push redirect-gateway def1 bypass-dhcp`, meaning that after establishing the VPN connection, all traffic will go through the VPN. This might cause problems if you use local DNS recursors which are not directly reachable, since you will try to reach them through the VPN and they might not answer to you. If that happens, use public DNS resolvers like those of OpenDNS (`208.67.222.222` and `208.67.220.220`) or Google (`8.8.4.4` and `8.8.8.8`). 128 | 129 | If you wish to use your local DNS server (Pi-Hile?), you have to modify a [dns-configuration](https://github.com/d3vilh/openvpn-server/tree/main/config/server.conf#L21) with your local DNS IP address. 130 | 131 | This also can be done easy via `"Configuration" > "OpenVPN Server" > "Push DHCP"` options on OpenVPN UI webpage. 132 | 133 | ### OpenVPN client subnets. Guest and Home users 134 | 135 | By default [d3vilh/openvpn-server](https://github.com/d3vilh/openvpn-server) OpenVPN server uses option `server 10.0.70.0/24` as **"Trusted"** subnet to grab dynamic IPs for all your Clients which, by default will have full access to your **"Private/Home"** subnet, as well as Internet over VPN. 136 | However you can be desired to share internet over VPN with specific, Guest Clients and restrict access to your **"Private/Home"** subnet. For this scenario [d3vilh/openvpn-server](https://github.com/d3vilh/openvpn-server) `server.conf` configuration file has special `route 10.0.71.0/24` option, aka **"Guest users"** subnet. 137 | 138 |

139 | OpenVPN Subnets 140 |

141 | 142 | To assign desired subnet policy to the specific client, you have to define static IP address for the client during its profile/Certificate creation. 143 | To do that, just enter `"Static IP (optional)"` field in `"Certificates"` page and press `"Create"` button. 144 | 145 | > Keep in mind, by default, all the clients have full access, so you don't need to specifically configure static IP for your own devices, your home devices always will land to **"Trusted"** subnet by default. 146 | 147 | ### Firewall rules 148 | 149 | By default `docker_entrypoint.sh` of [d3vilh/openvpn-server](https://github.com/d3vilh/openvpn-server) OpenVPN Server container will apply following Firewall rules: 150 | 151 | ```shell 152 | IPT MASQ Chains: 153 | MASQUERADE all -- ip-10-0-70-0.ec2.internal/24 anywhere 154 | MASQUERADE all -- ip-10-0-71-0.ec2.internal/24 anywhere 155 | IPT FWD Chains: 156 | 0 0 DROP 1 -- * * 10.0.71.0/24 0.0.0.0/0 icmptype 8 157 | 0 0 DROP 1 -- * * 10.0.71.0/24 0.0.0.0/0 icmptype 0 158 | 0 0 DROP 0 -- * * 10.0.71.0/24 192.168.88.0/24 159 | ``` 160 | 161 | You can apply optional Firewall rules in `~/openvpn-server/fw-rules.sh` file, which will be executed on the container start. 162 | 163 | Here is example of blocking all the traffic between 2 "Trusted" subnet clients: 164 | ```shell 165 | ~/openvpn-server $ cat fw-rules.sh 166 | iptables -A FORWARD -s 10.0.70.88 -d 10.0.70.77 -j DROP 167 | iptables -A FORWARD -d 10.0.70.77 -s 10.0.70.88 -j DROP 168 | ``` 169 | 170 | Check detailed subnets description on [here](https://github.com/d3vilh/openvpn-ui/tree/main#openvpn-client-subnets-guest-and-home-users). 171 | 172 | 173 | ### Generating .OVPN client profiles 174 |
175 | How to generate .OVPN client profile 176 | You can update external client IP and port address anytime under `"Configuration > OpenVPN Client"` menue. 177 | 178 | For this go to `"Configuration > OpenVPN Client"`: 179 | 180 | Configuration > Settings 181 | 182 | And then update `"Connection Address"` and `"Connection Port"` fields with your external Internet IP and Port. 183 | 184 | To generate new Client Certificate go to `"Certificates"`, then press `"Create Certificate"` button, enter new VPN client name, complete all the rest fields and press `"Create"` to generate new Client certificate: 185 | 186 | Server Address Create Certificate 187 | 188 | To download .OVPN client configuration file, press on the `Client Name` you just created: 189 | 190 | download OVPN 191 | 192 | Install [Official OpenVPN client](https://openvpn.net/vpn-client/) to your client device. 193 | 194 | Deliver .OVPN profile to the client device and import it as a FILE, then connect with new profile to enjoy your free VPN: 195 | 196 | PalmTX Import PalmTX Connected 197 | 198 |
199 | 200 | ### Renew Certificates for client profiles 201 |
202 | How to renew old client profile 203 | To renew certificate, go to `"Certificates"` and press `"Renew"` button for the client you would like to renew certificate for: 204 | 205 | Renew OpenVPN Certificate 206 | 207 | Right after this step new Certificate will be genrated and it will appear as new client profile with the same Client name. At this point both client profiles will have updated Certificate when you try to download it. 208 | 209 | Once you will deliver new client profile with renewed Certificate to you client, press `"Revoke"` button for old profile to revoke old Certificate, old client profile will be deleted from the list. 210 | 211 | If, for some reason you still would like to keep old certificate you have to `"Revoke"` new profile, old certificate will be rolled back and new profile will be deleted from the list. 212 | 213 | Renewal process will not affect active VPN connections, old client will be disconnected only after you revoke old certificate or certificate term of use will expire. 214 |
215 | 216 | ### Revoking .OVPN profiles 217 |
218 | How to revoke client certificate 219 | 220 | If you would like to prevent client to use yor VPN connection, you have to revoke client certificate and restart the OpenVPN daemon. 221 | You can do it via OpenVPN UI `"Certificates"` menue, by pressing `"Revoke"`` amber button: 222 | 223 | Revoke Certificate 224 | 225 | Certificate revoke won't kill active VPN connections, you'll have to restart the service if you want the user to immediately disconnect. It can be done from the same `"Certificates"` page, by pressing Restart red button: 226 | 227 | OpenVPN Restart 228 | 229 | You can do the same from the `"Maintenance"` page. 230 | 231 | After Revoking and Restarting the service, the client will be disconnected and will not be able to connect again with the same certificate. To delete the certificate from the server, you have to press `"Remove"` button. 232 |
233 | 234 | ### Two Factor Authentication (2FA) 235 | Starting from vestion `0.9.3` OpenVPN-UI has Two Factor Authentication (2FA) feature. 236 | OpenVPN-UI uses [oath-toolkit](https://savannah.nongnu.org/projects/oath-toolkit/) for two factor authentication. Means you don't need any ThirdParty 2FA provider. 237 | When generating 2FA-enabled certificates OpenVPN-UI will provide QR code with 2FA secret, which you can scan with your 2FA app (Google Authenticator [iOS](https://apps.apple.com/us/app/google-authenticator/id388497605), [Android](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&pcampaignid=web_share), Microsoft Authenticator [iOS](https://apps.apple.com/us/app/microsoft-authenticator/id983156458), [Android](https://play.google.com/store/apps/details?id=com.azure.authenticator&pcampaignid=web_share), etc) to get 2FA token for connection with this certificate. 238 | 239 | 2FA Certificates **`Renewal`**, **`Revoke`** and **`Delete`** process is the same as for regular certificates. 240 | 241 | #### To enable 2FA you have to: 242 | 243 | * Go to `"Configuration > OpenVPN Client"` page and enable `"Two Factor Authentication"` option to switch Certificates interface to 2FA mode, so you can generate certificates with 2FA enabled and access 2FA QR code for already generated certificates. 244 | 245 | > **Note**: You can generate 2FA-ready certificates at this stage, then deliver 2FA Certificates to all your client devices and enable 2FA Server support later, when you'll be ready to use it. Before that Server will still accept non 2FA-ready certificates only. 246 | 247 | * Go to `"Configuration > OpenVPN Server"` page and enable `"Two Factor Authentication"` option for OpenVPN Server backend. Once 2FA is enabled for Server, OpenVPN-Server **will allow 2FA connections only** (non 2FA-ready certificates won't connect). 248 | 249 | #### 2FA .OVPN profiles creation 250 |
251 | How to generate 2FA Certificate 252 | 253 | Procedure for 2FA generation is the same as for regular certificate, but you have to use the uniq `2FA Name` in the email-kind format: 254 | 255 | 2FA Certificate create 256 | 257 | > **Note**: For Multifactor Authentication (MFA), you can add one more password by completing **`Passphrase`** option. 258 | 259 | Both **`Passphrase`** and **`Client Static IP`** are optional parameters. 260 | 261 | When you complete all the fields, click on **`Create`** and your new 2FA Certificate will be ready. 262 | 263 | Once this done, you can click on the new certificate in the `Certificates` page to see all the details including QR code for 2FA token: 264 | 265 | 2FA Certificate details 266 | 267 | You can copy or email this information directly to happy 2FA certificate owner. 268 |
269 | 270 | #### 2FA certificates usage 271 |
272 | How to add 2FA profile to client 273 | 274 | To use 2FA certificate you have to install 2FA app on your device (**Google Authenticator** [iOS](https://apps.apple.com/us/app/google-authenticator/id388497605), [Android](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&pcampaignid=web_share), **Microsoft Authenticator** [iOS](https://apps.apple.com/us/app/microsoft-authenticator/id983156458), [Android](https://play.google.com/store/apps/details?id=com.azure.authenticator&pcampaignid=web_share), etc) and scan QR code from the `Certificates` details page. 275 | 276 | After scanning QR-code, new Authenticator profile will be created in your 2FA app with the same name as your 2FA Certificate name: 277 | 278 | 2FA Authenticator 279 | 280 | Then you have to download and deliver `.OVPN profile` to [OpenVPN Connect app](https://openvpn.net/client/) and open it as a file. Following window appear: 281 | 282 | 2FA OpenVPN Connect profile add 283 | 284 | Click `Add` to add new profile to OpenVPN Connect. Then you will be asked to enter your Username. As username use `2FA Name` which you used during Certificate/profile generation (as precisely as you can. `2FA Name` is part of authentication process): 285 | 286 | 2FA OpenVPN Connect profile username 287 | 288 | When you'll be prompted to Enter the password, you have to enter your 2FA token from your 2FA app: 289 | 290 | 2FA OpenVPN Connect profile 2FA password 291 | 292 | Connection will be suceeded if you entered `2FA Name` and 2FA token correctly. 293 | 294 | For MFA authentication you can use optional `Passphrase` when generating new Client certificate, to protect your 2FA token with additional password. In this case you have to enter your `Passphrase` as a `Private Key Password` and 2FA token as `Password`: 295 | 296 | 2FA OpenVPN Connect profile 2FA and Certificate passwords 297 | 298 |
299 | 300 | ### User Management 301 | 302 | You can create and delete users with different privileges - Administrators or regular users: 303 | * Administrators has full access 304 | * Regular users has access to Home page, Certificates and Logs pages only. This users can create, renew, revoke and delete all the certificates. 305 | 306 | 307 |
308 | How to manage OpenVPN-UI Users 309 | 310 | This functionality available via `"Users Profiles"` page: 311 | 312 | Username > Profile 313 | 314 | 315 | Then, if your user have enough privilegies you can Create new profile or manage profiles of other users: 316 | 317 | New OpenVPN UI Profile creation 318 | 319 | OpenVPN UI Profiles management 320 | 321 |
322 | 323 | ### OpenVPN Pstree structure 324 | 325 | All the Server and Client configuration located in Docker volume and can be easely tuned. Here are tree of volume content: 326 | 327 | ```shell 328 | |-- clients 329 | | |-- your_client1.ovpn 330 | |-- config 331 | | |-- client.conf 332 | | |-- easy-rsa.vars //EasyRSA vars draft, see below real vars file. 333 | | |-- server.conf 334 | |-- db 335 | | |-- data.db //OpenVPN UI DB 336 | |-- log 337 | | |-- openvpn.log 338 | |-- pki 339 | | |-- ca.crt 340 | | |-- vars // EasyRSA real vars, used by all applications 341 | | |-- certs_by_serial 342 | | | |-- your_client1_serial.pem 343 | | |-- crl.pem 344 | | |-- dh.pem 345 | | |-- index.txt 346 | | |-- ipp.txt 347 | | |-- issued 348 | | | |-- server.crt 349 | | | |-- your_client1.crt 350 | | |-- openssl-easyrsa.cnf 351 | | |-- private 352 | | | |-- ca.key 353 | | | |-- your_client1.key 354 | | | |-- server.key 355 | | |-- renewed 356 | | | |-- certs_by_serial 357 | | | |-- private_by_serial 358 | | | |-- reqs_by_serial 359 | | |-- reqs 360 | | | |-- server.req 361 | | | |-- your_client1.req 362 | | |-- revoked 363 | | | |-- certs_by_serial 364 | | | |-- private_by_serial 365 | | | |-- reqs_by_serial 366 | | |-- safessl-easyrsa.cnf 367 | | |-- serial 368 | | |-- ta.key 369 | |-- staticclients //Directory where stored all the satic clients configuration 370 | ``` 371 | 372 | 373 | 374 | ### Screenshots: 375 | 376 | OpenVPN-UI Login screen 377 | 378 | OpenVPN-UI Home screen 379 | 380 | OpenVPN-UI Certificates screen 381 | 382 | OpenVPN-UI Create Certificate screen 383 | 384 | OpenVPN-UI Expire Certificate details 385 | 386 | OpenVPN-UI OK Certificate details 387 | 388 | OpenVPN-UI EasyRSA vars screen 389 | 390 | OpenVPN-UI EasyRSA vars config view screen 391 | 392 | OpenVPN-UI Maintenance screen 393 | 394 | OpenVPN-UI Server Configuration screen 395 | 396 | OpenVPN-UI Server Configuration edit screen 397 | 398 | OpenVPN-UI Client Configuration screen 399 | 400 | OpenVPN-UI Configuration screen 401 | 402 | OpenVPN-UI User Profile 403 | 404 | New OpenVPN UI Profile creation 405 | 406 | OpenVPN UI Profiles management 407 | 408 | 409 | OpenVPN-UI Logs screen 410 | 411 | 412 | Build 22.01.2023 by [d3vilh](https://github.com/d3vilh) for small home project. 413 | 414 | -------------------------------------------------------------------------------- /templates/openvpn-dashboard.json.j2: -------------------------------------------------------------------------------- 1 | { 2 | "annotations": { 3 | "list": [ 4 | { 5 | "builtIn": 1, 6 | "datasource": { 7 | "type": "grafana", 8 | "uid": "-- Grafana --" 9 | }, 10 | "enable": true, 11 | "hide": true, 12 | "iconColor": "rgba(0, 211, 255, 1)", 13 | "name": "Annotations & Alerts", 14 | "target": { 15 | "limit": 100, 16 | "matchAny": false, 17 | "tags": [], 18 | "type": "dashboard" 19 | }, 20 | "type": "dashboard" 21 | } 22 | ] 23 | }, 24 | "description": "", 25 | "editable": true, 26 | "fiscalYearStartMonth": 0, 27 | "graphTooltip": 0, 28 | "links": [], 29 | "liveNow": false, 30 | "panels": [ 31 | { 32 | "datasource": { 33 | "type": "prometheus", 34 | "uid": "P1809F7CD0C75ACF3" 35 | }, 36 | "description": "", 37 | "fieldConfig": { 38 | "defaults": { 39 | "color": { 40 | "mode": "thresholds" 41 | }, 42 | "mappings": [ 43 | { 44 | "options": { 45 | "0": { 46 | "color": "dark-orange", 47 | "index": 1, 48 | "text": "STOPPING" 49 | }, 50 | "1": { 51 | "color": "dark-green", 52 | "index": 0, 53 | "text": "Server is UP" 54 | } 55 | }, 56 | "type": "value" 57 | }, 58 | { 59 | "options": { 60 | "match": "null", 61 | "result": { 62 | "color": "dark-red", 63 | "index": 2, 64 | "text": "Server is DOWN" 65 | } 66 | }, 67 | "type": "special" 68 | } 69 | ], 70 | "thresholds": { 71 | "mode": "absolute", 72 | "steps": [ 73 | { 74 | "color": "green", 75 | "value": null 76 | }, 77 | { 78 | "color": "red", 79 | "value": 80 80 | } 81 | ] 82 | } 83 | }, 84 | "overrides": [] 85 | }, 86 | "gridPos": { 87 | "h": 4, 88 | "w": 3, 89 | "x": 0, 90 | "y": 0 91 | }, 92 | "id": 16, 93 | "options": { 94 | "colorMode": "value", 95 | "graphMode": "area", 96 | "justifyMode": "auto", 97 | "orientation": "auto", 98 | "reduceOptions": { 99 | "calcs": [ 100 | "last" 101 | ], 102 | "fields": "/^Value$/", 103 | "values": false 104 | }, 105 | "textMode": "value" 106 | }, 107 | "pluginVersion": "10.2.0", 108 | "targets": [ 109 | { 110 | "datasource": { 111 | "type": "prometheus", 112 | "uid": "P1809F7CD0C75ACF3" 113 | }, 114 | "disableTextWrap": false, 115 | "editorMode": "builder", 116 | "exemplar": false, 117 | "expr": "openvpn_up - copenvpn_up", 118 | "format": "time_series", 119 | "fullMetaSearch": false, 120 | "includeNullMetadata": true, 121 | "instant": true, 122 | "interval": "15s", 123 | "legendFormat": "Count", 124 | "range": false, 125 | "refId": "A", 126 | "useBackend": false 127 | }, 128 | { 129 | "conditions": [ 130 | { 131 | "evaluator": { 132 | "params": [ 133 | 0, 134 | 0 135 | ], 136 | "type": "no_value" 137 | }, 138 | "operator": { 139 | "type": "when" 140 | }, 141 | "query": { 142 | "params": [ 143 | "A" 144 | ] 145 | }, 146 | "reducer": { 147 | "params": [], 148 | "type": "count_non_null" 149 | }, 150 | "type": "query" 151 | } 152 | ], 153 | "datasource": { 154 | "name": "Expression", 155 | "type": "__expr__", 156 | "uid": "__expr__" 157 | }, 158 | "expression": "if ", 159 | "hide": false, 160 | "refId": "B", 161 | "type": "classic_conditions" 162 | } 163 | ], 164 | "transparent": true, 165 | "type": "stat" 166 | }, 167 | { 168 | "datasource": { 169 | "type": "prometheus", 170 | "uid": "P1809F7CD0C75ACF3" 171 | }, 172 | "fieldConfig": { 173 | "defaults": { 174 | "color": { 175 | "mode": "thresholds" 176 | }, 177 | "mappings": [], 178 | "thresholds": { 179 | "mode": "absolute", 180 | "steps": [ 181 | { 182 | "color": "dark-green", 183 | "value": null 184 | } 185 | ] 186 | }, 187 | "unit": "short" 188 | }, 189 | "overrides": [] 190 | }, 191 | "gridPos": { 192 | "h": 4, 193 | "w": 3, 194 | "x": 3, 195 | "y": 0 196 | }, 197 | "id": 12, 198 | "options": { 199 | "colorMode": "value", 200 | "graphMode": "none", 201 | "justifyMode": "auto", 202 | "orientation": "horizontal", 203 | "reduceOptions": { 204 | "calcs": [ 205 | "last" 206 | ], 207 | "fields": "/^Connected$/", 208 | "values": false 209 | }, 210 | "text": { 211 | "titleSize": 25, 212 | "valueSize": 40 213 | }, 214 | "textMode": "value_and_name" 215 | }, 216 | "pluginVersion": "10.2.0", 217 | "targets": [ 218 | { 219 | "datasource": { 220 | "type": "prometheus", 221 | "uid": "P1809F7CD0C75ACF3" 222 | }, 223 | "editorMode": "code", 224 | "exemplar": false, 225 | "expr": "openvpn_server_connected_clients ", 226 | "instant": true, 227 | "legendFormat": "Connected", 228 | "range": false, 229 | "refId": "A" 230 | } 231 | ], 232 | "transparent": true, 233 | "type": "stat" 234 | }, 235 | { 236 | "datasource": { 237 | "type": "prometheus", 238 | "uid": "P1809F7CD0C75ACF3" 239 | }, 240 | "fieldConfig": { 241 | "defaults": { 242 | "color": { 243 | "mode": "thresholds" 244 | }, 245 | "mappings": [], 246 | "thresholds": { 247 | "mode": "absolute", 248 | "steps": [ 249 | { 250 | "color": "dark-orange", 251 | "value": null 252 | } 253 | ] 254 | }, 255 | "unit": "bytes" 256 | }, 257 | "overrides": [] 258 | }, 259 | "gridPos": { 260 | "h": 4, 261 | "w": 4, 262 | "x": 6, 263 | "y": 0 264 | }, 265 | "id": 20, 266 | "options": { 267 | "colorMode": "value", 268 | "graphMode": "none", 269 | "justifyMode": "auto", 270 | "orientation": "auto", 271 | "reduceOptions": { 272 | "calcs": [ 273 | "lastNotNull" 274 | ], 275 | "fields": "", 276 | "values": false 277 | }, 278 | "text": { 279 | "titleSize": 25, 280 | "valueSize": 50 281 | }, 282 | "textMode": "auto" 283 | }, 284 | "pluginVersion": "10.2.0", 285 | "targets": [ 286 | { 287 | "datasource": { 288 | "type": "prometheus", 289 | "uid": "P1809F7CD0C75ACF3" 290 | }, 291 | "editorMode": "code", 292 | "exemplar": false, 293 | "expr": "sum(increase(openvpn_server_client_received_bytes_total[1d]))", 294 | "instant": true, 295 | "legendFormat": "Total Received", 296 | "range": false, 297 | "refId": "A" 298 | } 299 | ], 300 | "transparent": true, 301 | "type": "stat" 302 | }, 303 | { 304 | "datasource": { 305 | "type": "prometheus", 306 | "uid": "P1809F7CD0C75ACF3" 307 | }, 308 | "description": "", 309 | "fieldConfig": { 310 | "defaults": { 311 | "color": { 312 | "mode": "thresholds" 313 | }, 314 | "decimals": 2, 315 | "mappings": [], 316 | "thresholds": { 317 | "mode": "absolute", 318 | "steps": [ 319 | { 320 | "color": "dark-yellow", 321 | "value": null 322 | } 323 | ] 324 | }, 325 | "unit": "bytes" 326 | }, 327 | "overrides": [] 328 | }, 329 | "gridPos": { 330 | "h": 4, 331 | "w": 4, 332 | "x": 10, 333 | "y": 0 334 | }, 335 | "id": 19, 336 | "options": { 337 | "colorMode": "value", 338 | "graphMode": "none", 339 | "justifyMode": "auto", 340 | "orientation": "auto", 341 | "reduceOptions": { 342 | "calcs": [ 343 | "lastNotNull" 344 | ], 345 | "fields": "", 346 | "values": false 347 | }, 348 | "text": { 349 | "titleSize": 25, 350 | "valueSize": 50 351 | }, 352 | "textMode": "auto" 353 | }, 354 | "pluginVersion": "10.2.0", 355 | "targets": [ 356 | { 357 | "datasource": { 358 | "type": "prometheus", 359 | "uid": "P1809F7CD0C75ACF3" 360 | }, 361 | "editorMode": "code", 362 | "exemplar": false, 363 | "expr": "sum(increase(openvpn_server_client_sent_bytes_total[1d]))", 364 | "instant": true, 365 | "legendFormat": "Total Sent", 366 | "range": false, 367 | "refId": "A" 368 | } 369 | ], 370 | "transparent": true, 371 | "type": "stat" 372 | }, 373 | { 374 | "datasource": { 375 | "type": "prometheus", 376 | "uid": "P1809F7CD0C75ACF3" 377 | }, 378 | "fieldConfig": { 379 | "defaults": { 380 | "color": { 381 | "mode": "palette-classic" 382 | }, 383 | "custom": { 384 | "hideFrom": { 385 | "legend": false, 386 | "tooltip": false, 387 | "viz": false 388 | } 389 | }, 390 | "decimals": 0, 391 | "mappings": [], 392 | "unit": "bytes" 393 | }, 394 | "overrides": [] 395 | }, 396 | "gridPos": { 397 | "h": 8, 398 | "w": 5, 399 | "x": 0, 400 | "y": 4 401 | }, 402 | "id": 22, 403 | "links": [], 404 | "maxDataPoints": 3, 405 | "options": { 406 | "displayLabels": [ 407 | "name", 408 | "percent" 409 | ], 410 | "legend": { 411 | "calcs": [], 412 | "displayMode": "table", 413 | "placement": "right", 414 | "showLegend": false, 415 | "values": [ 416 | "percent" 417 | ] 418 | }, 419 | "pieType": "pie", 420 | "reduceOptions": { 421 | "calcs": [ 422 | "lastNotNull" 423 | ], 424 | "fields": "", 425 | "values": false 426 | }, 427 | "tooltip": { 428 | "mode": "single", 429 | "sort": "none" 430 | } 431 | }, 432 | "targets": [ 433 | { 434 | "datasource": { 435 | "type": "prometheus", 436 | "uid": "P1809F7CD0C75ACF3" 437 | }, 438 | "editorMode": "code", 439 | "exemplar": false, 440 | "expr": "sort(sum(increase(openvpn_server_client_received_bytes_total{common_name!=\"UNDEF\"}[1d])+increase(openvpn_server_client_sent_bytes_total{common_name!=\"UNDEF\"}[1d]))by(common_name))", 441 | "format": "time_series", 442 | "instant": true, 443 | "intervalFactor": 1, 444 | "legendFormat": "{{ common_name }}", 445 | "range": false, 446 | "refId": "A" 447 | } 448 | ], 449 | "timeFrom": "24h", 450 | "title": "Traffic Distribution", 451 | "type": "piechart" 452 | }, 453 | { 454 | "columns": [], 455 | "datasource": { 456 | "type": "prometheus", 457 | "uid": "P1809F7CD0C75ACF3" 458 | }, 459 | "fontSize": "100%", 460 | "gridPos": { 461 | "h": 16, 462 | "w": 11, 463 | "x": 5, 464 | "y": 4 465 | }, 466 | "id": 23, 467 | "links": [], 468 | "pageSize": 100, 469 | "scroll": true, 470 | "showHeader": true, 471 | "sort": { 472 | "col": 2, 473 | "desc": true 474 | }, 475 | "styles": [ 476 | { 477 | "alias": "Time", 478 | "align": "auto", 479 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 480 | "pattern": "Time", 481 | "type": "hidden" 482 | }, 483 | { 484 | "alias": "Username", 485 | "align": "auto", 486 | "colors": [ 487 | "rgba(245, 54, 54, 0.9)", 488 | "rgba(237, 129, 40, 0.89)", 489 | "rgba(50, 172, 45, 0.97)" 490 | ], 491 | "decimals": 2, 492 | "pattern": "common_name", 493 | "thresholds": [], 494 | "type": "string", 495 | "unit": "short" 496 | }, 497 | { 498 | "alias": "Login Time", 499 | "align": "auto", 500 | "colors": [ 501 | "rgba(245, 54, 54, 0.9)", 502 | "rgba(237, 129, 40, 0.89)", 503 | "rgba(50, 172, 45, 0.97)" 504 | ], 505 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 506 | "mappingType": 1, 507 | "pattern": "connection_time", 508 | "thresholds": [], 509 | "type": "date", 510 | "unit": "dateTimeFromNow" 511 | }, 512 | { 513 | "alias": "Address", 514 | "align": "auto", 515 | "colors": [ 516 | "rgba(245, 54, 54, 0.9)", 517 | "rgba(237, 129, 40, 0.89)", 518 | "rgba(50, 172, 45, 0.97)" 519 | ], 520 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 521 | "decimals": 2, 522 | "mappingType": 1, 523 | "pattern": "virtual_address", 524 | "thresholds": [], 525 | "type": "string", 526 | "unit": "short" 527 | }, 528 | { 529 | "alias": "", 530 | "align": "auto", 531 | "colors": [ 532 | "rgba(245, 54, 54, 0.9)", 533 | "rgba(237, 129, 40, 0.89)", 534 | "rgba(50, 172, 45, 0.97)" 535 | ], 536 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 537 | "decimals": 2, 538 | "mappingType": 1, 539 | "pattern": "Value", 540 | "thresholds": [], 541 | "type": "hidden", 542 | "unit": "short" 543 | }, 544 | { 545 | "alias": "Rx", 546 | "align": "auto", 547 | "colors": [ 548 | "rgba(245, 54, 54, 0.9)", 549 | "rgba(237, 129, 40, 0.89)", 550 | "rgba(50, 172, 45, 0.97)" 551 | ], 552 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 553 | "decimals": 0, 554 | "mappingType": 1, 555 | "pattern": "Value #A", 556 | "thresholds": [], 557 | "type": "number", 558 | "unit": "bytes" 559 | }, 560 | { 561 | "alias": "Tx", 562 | "align": "auto", 563 | "colors": [ 564 | "rgba(245, 54, 54, 0.9)", 565 | "rgba(237, 129, 40, 0.89)", 566 | "rgba(50, 172, 45, 0.97)" 567 | ], 568 | "dateFormat": "YYYY-MM-DD HH:mm:ss", 569 | "decimals": 0, 570 | "mappingType": 1, 571 | "pattern": "Value #B", 572 | "thresholds": [], 573 | "type": "number", 574 | "unit": "bytes" 575 | } 576 | ], 577 | "targets": [ 578 | { 579 | "datasource": { 580 | "type": "prometheus", 581 | "uid": "P1809F7CD0C75ACF3" 582 | }, 583 | "editorMode": "code", 584 | "expr": "sort_desc(sum(label_replace(openvpn_server_client_received_bytes_total{common_name!=\"UNDEF\"}, \"connection_time\", \"${1}000\", \"connection_time\", \"(.*)\"))by(common_name, connection_time, virtual_address))", 585 | "format": "table", 586 | "instant": true, 587 | "intervalFactor": 1, 588 | "refId": "A" 589 | }, 590 | { 591 | "datasource": { 592 | "uid": "${DS_PROMETHEUS}" 593 | }, 594 | "expr": "sum(label_replace(openvpn_server_client_sent_bytes_total{common_name!=\"UNDEF\"}, \"connection_time\", \"${1}000\", \"connection_time\", \"(.*)\"))by(common_name, connection_time, virtual_address)", 595 | "format": "table", 596 | "instant": true, 597 | "intervalFactor": 1, 598 | "refId": "B" 599 | } 600 | ], 601 | "title": "Current OpenVPN Clients", 602 | "transform": "timeseries_to_rows", 603 | "type": "table-old" 604 | }, 605 | { 606 | "datasource": { 607 | "type": "prometheus", 608 | "uid": "P1809F7CD0C75ACF3" 609 | }, 610 | "fieldConfig": { 611 | "defaults": { 612 | "color": { 613 | "mode": "palette-classic" 614 | }, 615 | "mappings": [], 616 | "thresholds": { 617 | "mode": "absolute", 618 | "steps": [ 619 | { 620 | "color": "green", 621 | "value": null 622 | }, 623 | { 624 | "color": "red", 625 | "value": 80 626 | } 627 | ] 628 | }, 629 | "unit": "m" 630 | }, 631 | "overrides": [] 632 | }, 633 | "gridPos": { 634 | "h": 8, 635 | "w": 5, 636 | "x": 0, 637 | "y": 12 638 | }, 639 | "id": 8, 640 | "options": { 641 | "displayMode": "gradient", 642 | "minVizHeight": 10, 643 | "minVizWidth": 0, 644 | "namePlacement": "auto", 645 | "orientation": "horizontal", 646 | "reduceOptions": { 647 | "calcs": [ 648 | "max" 649 | ], 650 | "fields": "", 651 | "values": false 652 | }, 653 | "showUnfilled": true, 654 | "valueMode": "color" 655 | }, 656 | "pluginVersion": "10.2.0", 657 | "targets": [ 658 | { 659 | "datasource": { 660 | "type": "prometheus", 661 | "uid": "P1809F7CD0C75ACF3" 662 | }, 663 | "editorMode": "code", 664 | "exemplar": false, 665 | "expr": "(topk(6, sum by(common_name) (count_over_time(openvpn_server_client_received_bytes_total{common_name!=\"UNDEF\"}[24h]))) + topk(6, sum by(common_name) (count_over_time(openvpn_server_client_sent_bytes_total{common_name!=\"UNDEF\"}[24h]))))/2", 666 | "format": "time_series", 667 | "hide": false, 668 | "instant": false, 669 | "interval": "", 670 | "legendFormat": "{{common_name}}", 671 | "range": true, 672 | "refId": "A" 673 | } 674 | ], 675 | "timeFrom": "24h", 676 | "title": "Top Clients", 677 | "type": "bargauge" 678 | }, 679 | { 680 | "datasource": { 681 | "type": "prometheus", 682 | "uid": "P1809F7CD0C75ACF3" 683 | }, 684 | "fieldConfig": { 685 | "defaults": { 686 | "color": { 687 | "mode": "palette-classic" 688 | }, 689 | "mappings": [], 690 | "thresholds": { 691 | "mode": "absolute", 692 | "steps": [ 693 | { 694 | "color": "green" 695 | }, 696 | { 697 | "color": "red", 698 | "value": 80 699 | } 700 | ] 701 | }, 702 | "unit": "decbytes" 703 | }, 704 | "overrides": [] 705 | }, 706 | "gridPos": { 707 | "h": 8, 708 | "w": 5, 709 | "x": 0, 710 | "y": 20 711 | }, 712 | "id": 10, 713 | "options": { 714 | "displayMode": "gradient", 715 | "minVizHeight": 10, 716 | "minVizWidth": 0, 717 | "orientation": "horizontal", 718 | "reduceOptions": { 719 | "calcs": [ 720 | "max" 721 | ], 722 | "fields": "", 723 | "values": false 724 | }, 725 | "showUnfilled": true, 726 | "valueMode": "color" 727 | }, 728 | "pluginVersion": "10.1.0", 729 | "targets": [ 730 | { 731 | "datasource": { 732 | "type": "prometheus", 733 | "uid": "P1809F7CD0C75ACF3" 734 | }, 735 | "editorMode": "code", 736 | "exemplar": false, 737 | "expr": "topk(6, sum by(common_name) (increase(openvpn_server_client_received_bytes_total{common_name!=\"UNDEF\"}[24h]))) + topk(6, sum by(common_name) (increase(openvpn_server_client_sent_bytes_total{common_name!=\"UNDEF\"}[1d])))", 738 | "instant": false, 739 | "interval": "", 740 | "legendFormat": "{{common_name}}", 741 | "range": true, 742 | "refId": "A" 743 | } 744 | ], 745 | "timeFrom": "24h", 746 | "title": "Top Traffic", 747 | "type": "bargauge" 748 | }, 749 | { 750 | "datasource": { 751 | "type": "prometheus", 752 | "uid": "P1809F7CD0C75ACF3" 753 | }, 754 | "description": "", 755 | "fieldConfig": { 756 | "defaults": { 757 | "color": { 758 | "mode": "palette-classic" 759 | }, 760 | "custom": { 761 | "axisCenteredZero": false, 762 | "axisColorMode": "series", 763 | "axisLabel": "", 764 | "axisPlacement": "auto", 765 | "fillOpacity": 100, 766 | "gradientMode": "none", 767 | "hideFrom": { 768 | "legend": false, 769 | "tooltip": false, 770 | "viz": false 771 | }, 772 | "lineWidth": 1, 773 | "scaleDistribution": { 774 | "type": "linear" 775 | }, 776 | "thresholdsStyle": { 777 | "mode": "off" 778 | } 779 | }, 780 | "decimals": 0, 781 | "mappings": [], 782 | "min": 1, 783 | "thresholds": { 784 | "mode": "absolute", 785 | "steps": [ 786 | { 787 | "color": "green" 788 | }, 789 | { 790 | "color": "red", 791 | "value": 80 792 | } 793 | ] 794 | }, 795 | "unit": "short" 796 | }, 797 | "overrides": [] 798 | }, 799 | "gridPos": { 800 | "h": 8, 801 | "w": 11, 802 | "x": 5, 803 | "y": 20 804 | }, 805 | "id": 24, 806 | "links": [], 807 | "options": { 808 | "barRadius": 0.05, 809 | "barWidth": 0.95, 810 | "fullHighlight": false, 811 | "groupWidth": 1, 812 | "legend": { 813 | "calcs": [], 814 | "displayMode": "list", 815 | "placement": "bottom", 816 | "showLegend": true 817 | }, 818 | "orientation": "auto", 819 | "showValue": "always", 820 | "stacking": "none", 821 | "text": { 822 | "valueSize": 40 823 | }, 824 | "tooltip": { 825 | "mode": "single", 826 | "sort": "desc" 827 | }, 828 | "xTickLabelRotation": 0, 829 | "xTickLabelSpacing": 0 830 | }, 831 | "pluginVersion": "10.0.3", 832 | "targets": [ 833 | { 834 | "datasource": { 835 | "type": "prometheus", 836 | "uid": "P1809F7CD0C75ACF3" 837 | }, 838 | "editorMode": "code", 839 | "expr": "topk(6, sum(count_values(\"connection_time\", count_over_time(openvpn_server_client_received_bytes_total{common_name!=\"UNDEF\"}[1d]))by(common_name))by(common_name)>1)", 840 | "format": "time_series", 841 | "instant": true, 842 | "intervalFactor": 1, 843 | "legendFormat": "{{ common_name }}", 844 | "refId": "A" 845 | } 846 | ], 847 | "timeFrom": "24h", 848 | "title": "Connection attempts", 849 | "type": "barchart" 850 | }, 851 | { 852 | "datasource": { 853 | "type": "prometheus", 854 | "uid": "P1809F7CD0C75ACF3" 855 | }, 856 | "description": "Number of OpenVPN Connections", 857 | "fieldConfig": { 858 | "defaults": { 859 | "color": { 860 | "fixedColor": "dark-green", 861 | "mode": "fixed" 862 | }, 863 | "custom": { 864 | "axisCenteredZero": false, 865 | "axisColorMode": "text", 866 | "axisGridShow": false, 867 | "axisLabel": "Clients", 868 | "axisPlacement": "left", 869 | "barAlignment": 0, 870 | "drawStyle": "line", 871 | "fillOpacity": 100, 872 | "gradientMode": "none", 873 | "hideFrom": { 874 | "legend": false, 875 | "tooltip": false, 876 | "viz": false 877 | }, 878 | "insertNulls": false, 879 | "lineInterpolation": "smooth", 880 | "lineStyle": { 881 | "fill": "solid" 882 | }, 883 | "lineWidth": 2, 884 | "pointSize": 5, 885 | "scaleDistribution": { 886 | "type": "linear" 887 | }, 888 | "showPoints": "auto", 889 | "spanNulls": false, 890 | "stacking": { 891 | "group": "A", 892 | "mode": "none" 893 | }, 894 | "thresholdsStyle": { 895 | "mode": "off" 896 | } 897 | }, 898 | "mappings": [], 899 | "thresholds": { 900 | "mode": "absolute", 901 | "steps": [ 902 | { 903 | "color": "dark-green" 904 | } 905 | ] 906 | } 907 | }, 908 | "overrides": [] 909 | }, 910 | "gridPos": { 911 | "h": 4, 912 | "w": 16, 913 | "x": 0, 914 | "y": 28 915 | }, 916 | "id": 2, 917 | "options": { 918 | "legend": { 919 | "calcs": [], 920 | "displayMode": "list", 921 | "placement": "right", 922 | "showLegend": false 923 | }, 924 | "tooltip": { 925 | "mode": "single", 926 | "sort": "none" 927 | } 928 | }, 929 | "pluginVersion": "9.2.1", 930 | "targets": [ 931 | { 932 | "datasource": { 933 | "type": "prometheus", 934 | "uid": "P1809F7CD0C75ACF3" 935 | }, 936 | "editorMode": "code", 937 | "expr": "openvpn_server_connected_clients", 938 | "legendFormat": "Connections", 939 | "range": true, 940 | "refId": "A" 941 | } 942 | ], 943 | "type": "timeseries" 944 | }, 945 | { 946 | "datasource": { 947 | "type": "prometheus", 948 | "uid": "P1809F7CD0C75ACF3" 949 | }, 950 | "description": "Clients connection timeline", 951 | "fieldConfig": { 952 | "defaults": { 953 | "color": { 954 | "mode": "palette-classic" 955 | }, 956 | "custom": { 957 | "fillOpacity": 100, 958 | "hideFrom": { 959 | "legend": false, 960 | "tooltip": false, 961 | "viz": false 962 | }, 963 | "insertNulls": false, 964 | "lineWidth": 1, 965 | "spanNulls": false 966 | }, 967 | "mappings": [], 968 | "thresholds": { 969 | "mode": "percentage", 970 | "steps": [ 971 | { 972 | "color": "green" 973 | } 974 | ] 975 | }, 976 | "unit": "short" 977 | }, 978 | "overrides": [] 979 | }, 980 | "gridPos": { 981 | "h": 7, 982 | "w": 16, 983 | "x": 0, 984 | "y": 32 985 | }, 986 | "id": 6, 987 | "options": { 988 | "alignValue": "center", 989 | "legend": { 990 | "displayMode": "list", 991 | "placement": "bottom", 992 | "showLegend": false 993 | }, 994 | "mergeValues": false, 995 | "rowHeight": 0.98, 996 | "showValue": "never", 997 | "tooltip": { 998 | "mode": "single", 999 | "sort": "none" 1000 | } 1001 | }, 1002 | "pluginVersion": "9.2.1", 1003 | "targets": [ 1004 | { 1005 | "datasource": { 1006 | "type": "prometheus", 1007 | "uid": "P1809F7CD0C75ACF3" 1008 | }, 1009 | "editorMode": "code", 1010 | "exemplar": false, 1011 | "expr": "count by(common_name) (openvpn_server_client_sent_bytes_total{common_name!=\"UNDEF\"})", 1012 | "format": "heatmap", 1013 | "instant": false, 1014 | "interval": "", 1015 | "legendFormat": "{{common_name}}", 1016 | "range": true, 1017 | "refId": "A" 1018 | } 1019 | ], 1020 | "title": "Connection timeline", 1021 | "type": "state-timeline" 1022 | }, 1023 | { 1024 | "datasource": { 1025 | "type": "prometheus", 1026 | "uid": "P1809F7CD0C75ACF3" 1027 | }, 1028 | "description": "", 1029 | "fieldConfig": { 1030 | "defaults": { 1031 | "color": { 1032 | "mode": "palette-classic" 1033 | }, 1034 | "custom": { 1035 | "axisCenteredZero": false, 1036 | "axisColorMode": "text", 1037 | "axisLabel": "", 1038 | "axisPlacement": "auto", 1039 | "barAlignment": 0, 1040 | "drawStyle": "line", 1041 | "fillOpacity": 2, 1042 | "gradientMode": "none", 1043 | "hideFrom": { 1044 | "legend": false, 1045 | "tooltip": false, 1046 | "viz": false 1047 | }, 1048 | "insertNulls": false, 1049 | "lineInterpolation": "smooth", 1050 | "lineStyle": { 1051 | "fill": "solid" 1052 | }, 1053 | "lineWidth": 2, 1054 | "pointSize": 5, 1055 | "scaleDistribution": { 1056 | "type": "linear" 1057 | }, 1058 | "showPoints": "auto", 1059 | "spanNulls": false, 1060 | "stacking": { 1061 | "group": "A", 1062 | "mode": "none" 1063 | }, 1064 | "thresholdsStyle": { 1065 | "mode": "off" 1066 | } 1067 | }, 1068 | "mappings": [], 1069 | "thresholds": { 1070 | "mode": "absolute", 1071 | "steps": [ 1072 | { 1073 | "color": "green" 1074 | }, 1075 | { 1076 | "color": "red", 1077 | "value": 80 1078 | } 1079 | ] 1080 | }, 1081 | "unit": "decbytes" 1082 | }, 1083 | "overrides": [] 1084 | }, 1085 | "gridPos": { 1086 | "h": 6, 1087 | "w": 16, 1088 | "x": 0, 1089 | "y": 39 1090 | }, 1091 | "id": 4, 1092 | "options": { 1093 | "legend": { 1094 | "calcs": [], 1095 | "displayMode": "list", 1096 | "placement": "bottom", 1097 | "showLegend": true 1098 | }, 1099 | "tooltip": { 1100 | "mode": "single", 1101 | "sort": "none" 1102 | } 1103 | }, 1104 | "pluginVersion": "9.2.1", 1105 | "targets": [ 1106 | { 1107 | "datasource": { 1108 | "type": "prometheus", 1109 | "uid": "P1809F7CD0C75ACF3" 1110 | }, 1111 | "editorMode": "code", 1112 | "exemplar": false, 1113 | "expr": "openvpn_server_client_sent_bytes_total{common_name!=\"UNDEF\"}", 1114 | "format": "time_series", 1115 | "instant": false, 1116 | "interval": "", 1117 | "legendFormat": "{{common_name}} download", 1118 | "range": true, 1119 | "refId": "A" 1120 | }, 1121 | { 1122 | "datasource": { 1123 | "type": "prometheus", 1124 | "uid": "P1809F7CD0C75ACF3" 1125 | }, 1126 | "editorMode": "code", 1127 | "exemplar": false, 1128 | "expr": "openvpn_server_client_received_bytes_total{common_name!=\"UNDEF\"}", 1129 | "hide": false, 1130 | "instant": false, 1131 | "interval": "", 1132 | "legendFormat": "{{common_name}} upload", 1133 | "range": true, 1134 | "refId": "B" 1135 | } 1136 | ], 1137 | "title": "Clients Traffic", 1138 | "type": "timeseries" 1139 | } 1140 | ], 1141 | "refresh": "10s", 1142 | "schemaVersion": 38, 1143 | "tags": [ 1144 | "openvpn", 1145 | "raspberry", 1146 | "raspberry-gateway", 1147 | "d3vilh" 1148 | ], 1149 | "templating": { 1150 | "list": [] 1151 | }, 1152 | "time": { 1153 | "from": "now-24h", 1154 | "to": "now" 1155 | }, 1156 | "timepicker": {}, 1157 | "timezone": "", 1158 | "title": "OpenVPN", 1159 | "uid": "58l7kyvWz", 1160 | "version": 1, 1161 | "weekStart": "" 1162 | } --------------------------------------------------------------------------------