From ed6981bed248d8d582b18622b715ba28f3b2d958 Mon Sep 17 00:00:00 2001 From: AlexV Date: Wed, 12 Nov 2025 18:17:48 +0100 Subject: [PATCH 1/5] [fix] Fixes to molecule setup. --- meta/runtime.yml | 4 ++++ molecule/default/molecule.yml | 2 +- molecule/local/molecule.yml | 3 ++- molecule/resources/collections.yml | 4 ++++ 4 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 meta/runtime.yml create mode 100644 molecule/resources/collections.yml diff --git a/meta/runtime.yml b/meta/runtime.yml new file mode 100644 index 00000000..96d46335 --- /dev/null +++ b/meta/runtime.yml @@ -0,0 +1,4 @@ +--- +requires_ansible: ">=2.13" +collections: + - community.general diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml index 05304a25..f813b79a 100644 --- a/molecule/default/molecule.yml +++ b/molecule/default/molecule.yml @@ -28,7 +28,7 @@ provisioner: verify: ../resources/verify.yml config_options: defaults: - stdout_callback: yaml + result_format: yaml bin_ansible_callbacks: true inventory: host_vars: diff --git a/molecule/local/molecule.yml b/molecule/local/molecule.yml index 4ff9991d..13b13bba 100644 --- a/molecule/local/molecule.yml +++ b/molecule/local/molecule.yml @@ -4,6 +4,7 @@ dependency: name: galaxy options: role-file: molecule/resources/requirements.yml + requirements-file: molecule/resources/collections.yml driver: name: docker lint: | @@ -60,7 +61,7 @@ provisioner: verify: ../resources/verify.yml config_options: defaults: - stdout_callback: yaml + result_format: yaml bin_ansible_callbacks: true verifier: name: ansible diff --git a/molecule/resources/collections.yml b/molecule/resources/collections.yml new file mode 100644 index 00000000..62f9bcc5 --- /dev/null +++ b/molecule/resources/collections.yml @@ -0,0 +1,4 @@ +--- +collections: + - name: community.general + version: ">=10.5.0" \ No newline at end of file From 90ad8146d62daa1112cd5a942f203b6a63d82493 Mon Sep 17 00:00:00 2001 From: AlexV Date: Wed, 12 Nov 2025 18:18:25 +0100 Subject: [PATCH 2/5] [change] Simplify running ./load_initial_data.py --- templates/load_initial_data.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/load_initial_data.py b/templates/load_initial_data.py index 6428d267..11b6df3b 100644 --- a/templates/load_initial_data.py +++ b/templates/load_initial_data.py @@ -38,8 +38,8 @@ print("default site updated") # Get SSH key pair -ssh_private_key = os.environ.get("PRIVATE_KEY") -ssh_pub_key = os.environ.get("PUBLIC_KEY") +ssh_private_key = os.environ.get("PRIVATE_KEY", "") +ssh_pub_key = os.environ.get("PUBLIC_KEY", "") # Create a default credentials object if ssh_private_key and Credentials.objects.count() == 0: From 0b2a9bd89b44054f900f4ee6c9848a6217478683 Mon Sep 17 00:00:00 2001 From: AlexV Date: Thu, 13 Nov 2025 10:24:12 +0100 Subject: [PATCH 3/5] [change] Create CA and VPN with load_initial_data --- templates/load_initial_data.py | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/templates/load_initial_data.py b/templates/load_initial_data.py index 11b6df3b..3ba2606d 100644 --- a/templates/load_initial_data.py +++ b/templates/load_initial_data.py @@ -20,6 +20,9 @@ Credentials = load_model("connection", "Credentials") Template = load_model("config", "Template") +Ca = load_model("pki", "Ca") +Vpn = load_model("config", "Vpn") + User = get_user_model() changed = False @@ -81,3 +84,35 @@ config_file["contents"] += "\n" + ssh_pub_key template_obj.save() print(f"changed {template_obj.name} to add default SSH credential") + + +# Create Ca +if len(Ca.objects.all()) == 0: + ca_instance = Ca.objects.create( + name="{{ inventory_hostname }} CA", + ) + print("created {{ inventory_hostname }} Certificate Authority") +else: + # If Ca already exists, get the first one + ca_instance = Ca.objects.first() + +# Create Vpn +if len(Vpn.objects.all()) == 0: + Vpn.objects.create( + name="{{ inventory_hostname }} Vpn", + host="{{ inventory_hostname }}", + backend="openwisp_controller.vpn_backends.OpenVpn", + ca=ca_instance, + config={ + "openvpn": [{ + "server": "10.42.0.0 255.255.255.0", + "name": "{{ inventory_hostname }}-vpn", + "mode": "server", + "dev_type": "tun", + "dev": "tun0", + 'proto': 'udp', + 'tls_server': True, + }] + } + ) + print("created Default Vpn Server") \ No newline at end of file From 6743c2a021a4a99cdd44da97f803a505a88fd00d Mon Sep 17 00:00:00 2001 From: AlexV Date: Fri, 14 Nov 2025 11:26:16 +0100 Subject: [PATCH 4/5] [change] Deploy VPN server config --- defaults/main.yml | 2 + tasks/django.yml | 6 ++ tasks/main.yml | 19 +++++++ tasks/openvpn.yml | 56 +++++++++++++++++++ templates/load_initial_data.py | 95 ++++++++++++++++++++++++++++++-- templates/logrotate.d/openvpn.j2 | 9 +++ 6 files changed, 181 insertions(+), 6 deletions(-) create mode 100644 tasks/openvpn.yml create mode 100644 templates/logrotate.d/openvpn.j2 diff --git a/defaults/main.yml b/defaults/main.yml index ea55373d..6b32705c 100755 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -234,3 +234,5 @@ openwisp2_radius_periodic_tasks: true openwisp2_usage_metric_collection_periodic_tasks: true # point {{ inventory_name }} to localhost in /etc/hosts openwisp2_inventory_hostname_localhost: true +# installs and configure openvpn to provide a Management IP to all devices +openwisp2_openvpn_setup: true \ No newline at end of file diff --git a/tasks/django.yml b/tasks/django.yml index fd45abf1..29d8c33b 100644 --- a/tasks/django.yml +++ b/tasks/django.yml @@ -228,3 +228,9 @@ or "changed" in load_initial_data_result.stdout args: chdir: "{{ openwisp2_path }}" + +- name: Get openwisp2_vpn_id and openwisp2_vpn_name + set_fact: + openwisp2_vpn_id: "{{ load_initial_data_result.stdout | regex_search('openwisp2_vpn_id\\s+=\\s+([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})','\\1') | first }}" + openwisp2_vpn_name: "{{ load_initial_data_result.stdout | regex_search('openwisp2_vpn_name\\s+=\\s+(.*)','\\1') | first }}" + when: '"created" in load_initial_data_result.stdout and openwisp2_openvpn_setup' diff --git a/tasks/main.yml b/tasks/main.yml index c69d055d..71549fe7 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -40,5 +40,24 @@ - import_tasks: cron.yml tags: [openwisp2, cron] +# Making sure bare necessary services are running +- name: Update supervisor configuration + command: supervisorctl update openwisp2 + register: supervisord_update_openwisp2 + when: openwisp2_openvpn_setup + +- name: Restart openwisp2 uwsgi server + command: "supervisorctl restart openwisp2" + when: openwisp2_openvpn_setup + +- name: Ensure nginx is started + service: + name: nginx + state: started + when: openwisp2_nginx_install + +- import_tasks: openvpn.yml + tags: [openwisp2, openvpn] + - import_tasks: complete.yml tags: [openwisp2] diff --git a/tasks/openvpn.yml b/tasks/openvpn.yml new file mode 100644 index 00000000..be2c1e25 --- /dev/null +++ b/tasks/openvpn.yml @@ -0,0 +1,56 @@ +--- +- name: Install OpenVPN if needed + apt: + name: + - openvpn + state: present + update_cache: true + when: openwisp2_openvpn_setup and openwisp2_vpn_id is defined + # Note: openwisp2_vpn_id will be defined only in the ansible run that creates it. + +- name: Configure logrotate for OpenVPN + template: + src: logrotate.d/openvpn.j2 + dest: /etc/logrotate.d/openvpn + mode: '0644' + when: openwisp2_openvpn_setup and openwisp2_vpn_id is defined + +- name: Retrieve Access Token with default credentials + ansible.builtin.uri: + url: "https://{{ inventory_hostname }}/api/v1/users/token/" + method: POST + status_code: 200 + headers: + Content-Type: application/json + body_format: json + body: + username: "admin" + password: "admin" + validate_certs: false + follow_redirects: all + when: openwisp2_openvpn_setup and openwisp2_vpn_id is defined + register: login_task_default_result + +- name: Retrieve OpenVPN Server configuration + ansible.builtin.get_url: + url: "https://{{ inventory_hostname }}/api/v1/controller/vpn/{{ openwisp2_vpn_id }}/configuration/" + dest: "/etc/openvpn/{{ openwisp2_vpn_name }}-config.tar.gz" + validate_certs: false + headers: + Content-Type: application/json + Authorization: Bearer {{ login_task_default_result.json.token }} + when: openwisp2_openvpn_setup and openwisp2_vpn_id is defined and openwisp2_vpn_name is defined + +- name: Unarchive OpenVPN Server configuration + unarchive: + src: "/etc/openvpn/{{ openwisp2_vpn_name }}-config.tar.gz" + dest: /etc/openvpn/server/ + remote_src: yes + when: openwisp2_openvpn_setup and openwisp2_vpn_id is defined and openwisp2_vpn_name is defined + +- name: Enable and start OpenVPN service + systemd: + name: "openvpn-server@{{ openwisp2_vpn_name }}" + enabled: true + state: started + when: openwisp2_openvpn_setup and openwisp2_vpn_name is defined \ No newline at end of file diff --git a/templates/load_initial_data.py b/templates/load_initial_data.py index 3ba2606d..3d42b6d4 100644 --- a/templates/load_initial_data.py +++ b/templates/load_initial_data.py @@ -98,8 +98,8 @@ # Create Vpn if len(Vpn.objects.all()) == 0: - Vpn.objects.create( - name="{{ inventory_hostname }} Vpn", + vpn_instance = Vpn.objects.create( + name="{{ inventory_hostname }}-vpn", host="{{ inventory_hostname }}", backend="openwisp_controller.vpn_backends.OpenVpn", ca=ca_instance, @@ -108,11 +108,94 @@ "server": "10.42.0.0 255.255.255.0", "name": "{{ inventory_hostname }}-vpn", "mode": "server", + 'proto': 'udp', + "port": 1194, "dev_type": "tun", "dev": "tun0", - 'proto': 'udp', - 'tls_server': True, - }] + "local": "", + "comp_lzo": "adaptive", + "auth": "SHA1", + "data_ciphers": [ + { + "cipher": "AES-256-GCM", + "optional": False + }, + { + "cipher": "AES-128-GCM", + "optional": False + } + ], + "data_ciphers_fallback": "AES-256-GCM", + "cipher": "AES-256-GCM", + "engine": "", + "ca": "ca.pem", + "cert": "cert.pem", + "key": "key.pem", + "pkcs12": "", + "tls_auth": "", + "ns_cert_type": "", + "mtu_disc": "no", + "mtu_test": False, + "fragment": 0, + "mssfix": 1450, + "keepalive": "", + "persist_tun": False, + "persist_key": False, + "tun_ipv6": False, + "up": "", + "up_delay": 0, + "down": "", + "script_security": 1, + "user": "", + "group": "", + "mute": 0, + "status": "", + "status_version": 1, + "mute_replay_warnings": False, + "secret": "", + "reneg_sec": 3600, + "tls_timeout": 2, + "tls_cipher": "", + "remote_cert_tls": "", + "float": False, + "auth_nocache": False, + "fast_io": False, + "log": "", + "verb": 1, + "topology": "subnet", + "tls_server": True, + "dh": "dh.pem", + "crl_verify": "", + "duplicate_cn": False, + "client_to_client": False, + "client_cert_not_required": False, + "username_as_common_name": False, + "auth_user_pass_verify": "" + }], + "files": [ + { + "path": "ca.pem", + "mode": "0644", + "contents": "{{ '{{ ca }}' }}" + }, + { + "path": "cert.pem", + "mode": "0644", + "contents": "{{ '{{ cert }}' }}" + }, + { + "path": "key.pem", + "mode": "0644", + "contents": "{{ '{{ key }}' }}" + }, + { + "path": "dh.pem", + "mode": "0644", + "contents": "{{ '{{ dh }}' }}" + } + ] } ) - print("created Default Vpn Server") \ No newline at end of file + print(f"created {{ inventory_hostname }} Vpn Server") + print(f"openwisp2_vpn_name = {vpn_instance.name}") + print(f"openwisp2_vpn_id = {vpn_instance.id}") \ No newline at end of file diff --git a/templates/logrotate.d/openvpn.j2 b/templates/logrotate.d/openvpn.j2 new file mode 100644 index 00000000..2ff15554 --- /dev/null +++ b/templates/logrotate.d/openvpn.j2 @@ -0,0 +1,9 @@ +/var/log/openvpn/*.log { + daily + missingok + rotate 30 + compress + delaycompress + notifempty + copytruncate + } From c73d3a0b5e4b82befe3f4d6ee7340f824fc402f8 Mon Sep 17 00:00:00 2001 From: AlexV Date: Fri, 14 Nov 2025 12:04:29 +0100 Subject: [PATCH 5/5] [fix] Restart uwsgi only if vpn is being created --- tasks/main.yml | 16 ---------------- tasks/openvpn.yml | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tasks/main.yml b/tasks/main.yml index 71549fe7..7b685a90 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -40,22 +40,6 @@ - import_tasks: cron.yml tags: [openwisp2, cron] -# Making sure bare necessary services are running -- name: Update supervisor configuration - command: supervisorctl update openwisp2 - register: supervisord_update_openwisp2 - when: openwisp2_openvpn_setup - -- name: Restart openwisp2 uwsgi server - command: "supervisorctl restart openwisp2" - when: openwisp2_openvpn_setup - -- name: Ensure nginx is started - service: - name: nginx - state: started - when: openwisp2_nginx_install - - import_tasks: openvpn.yml tags: [openwisp2, openvpn] diff --git a/tasks/openvpn.yml b/tasks/openvpn.yml index be2c1e25..9bb20b11 100644 --- a/tasks/openvpn.yml +++ b/tasks/openvpn.yml @@ -1,4 +1,20 @@ --- +# Making sure bare necessary services are running +- name: Update supervisor configuration + command: supervisorctl update openwisp2 + register: supervisord_update_openwisp2 + when: openwisp2_openvpn_setup and openwisp2_vpn_id is defined + +- name: Restart openwisp2 uwsgi server + command: "supervisorctl restart openwisp2" + when: openwisp2_openvpn_setup and openwisp2_vpn_id is defined + +- name: Ensure nginx is started + service: + name: nginx + state: started + when: openwisp2_nginx_install + - name: Install OpenVPN if needed apt: name: