From 092655449be7cf7f121f399168cb1363d79fcc0e Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Tue, 18 Nov 2025 13:19:58 +0100 Subject: [PATCH 1/3] create a virtual test device --- test_vm/README.MD | 104 +++++++++++++++++++++++++++++++++++++++++++ test_vm/delete_vm.sh | 60 +++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 test_vm/README.MD create mode 100755 test_vm/delete_vm.sh diff --git a/test_vm/README.MD b/test_vm/README.MD new file mode 100644 index 000000000..37be1891e --- /dev/null +++ b/test_vm/README.MD @@ -0,0 +1,104 @@ +# Complete Step-by-Step Guide: Vagrant + libvirt + Manual DHCP + +This guide will help you set up a Vagrant VM with two network interfaces: +- **First interface:** NAT (default, for SSH access) +- **Second interface:** Directly connected to the host via an isolated bridge (e.g., enbr99), does not get an IP automatically, and will only receive an IP from your custom DHCP server running in Docker on the host. + +--- + +## 1. Install Required Packages and Dependencies + +```bash +sudo apt-get update +sudo apt-get install -y qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virt-manager \ + ruby ruby-dev libvirt-dev zlib1g-dev ebtables dnsmasq-base vagrant + +``` + +## 2. Add Your User to the libvirt Group + +```bash +sudo usermod -aG libvirt $(whoami) +newgrp libvirt +``` + +Note: After adding yourself to the libvirt group, restart your session or use newgrp libvirt for the permissions to take effect. + + +## 3. Check and Start libvirt Service + +```bash +systemctl status libvirtd +sudo systemctl start libvirtd +``` + +## 4. Install the vagrant-libvirt Plugin + +```bash +vagrant plugin install vagrant-libvirt +``` + +## 5. Remove Old Bridge (enbr99) If It Exists + +```bash +ip link show enbr99 +sudo ip link delete enbr99 type bridge +``` + +## 6. Create an Isolated libvirt Network Without IP and DHCP + +### Create the file /tmp/hostonly-noip.xml: + +```xml + + hostonly-noip + + + +``` + +### Create and start the network: + +```bash +sudo virsh net-define /tmp/hostonly-noip.xml +sudo virsh net-start hostonly-noip +sudo virsh net-autostart hostonly-noip +``` + +## 7. Verify the Network is Active + +```bash +sudo virsh net-list --all +``` + +You should see hostonly-noip with status active. + +## 8. Create Your Vagrantfile + +In your project directory, create a file named Vagrantfile: + +```ruby +Vagrant.configure("2") do |config| + config.vm.box = "bento/ubuntu-24.04" + config.vm.hostname = "ubuntu-vm" + # First interface: NAT (default, for SSH) + # Second interface: direct to host via enbr99, no auto-IP + config.vm.network "private_network", + libvirt__network_name: "hostonly-noip", + auto_config: false, + mac: "52:54:00:12:34:56" + config.vm.provider :libvirt do |libvirt| + libvirt.memory = 2048 + libvirt.cpus = 2 + libvirt.graphics_type = "none" + end +end +``` + +## 9. Start the Virtual Machine + + +```bash +vagrant up --provider=libvirt +``` +If you see "Permission denied to /var/run/libvirt/libvirt-sock", check your libvirt group membership and restart your session. \ No newline at end of file diff --git a/test_vm/delete_vm.sh b/test_vm/delete_vm.sh new file mode 100755 index 000000000..3fa9e62ff --- /dev/null +++ b/test_vm/delete_vm.sh @@ -0,0 +1,60 @@ +#!/bin/sh + +echo "Stopping and removing all Vagrant machines..." +# Destroy all Vagrant VMs found in global status +vagrant global-status --prune | awk '/libvirt|virtualbox/ {print $1}' | while read id; do + vagrant destroy -f "$id" +done + +echo "Removing all Vagrant boxes..." +# Remove all Vagrant boxes +vagrant box list | awk '{print $1}' | while read box; do + vagrant box remove -f "$box" +done + +echo "Removing all Vagrant plugins..." +# Uninstall all Vagrant plugins +vagrant plugin list | awk '{print $1}' | while read plugin; do + vagrant plugin uninstall "$plugin" +done + +echo "Deleting Vagrant settings and cache..." +# Remove Vagrant configuration and cache directory +rm -rf ~/.vagrant.d + +echo "Uninstalling Vagrant..." +# Remove Vagrant package +sudo apt-get remove --purge -y vagrant || sudo dpkg -r vagrant + +echo "Removing all libvirt virtual machines..." +# Destroy and undefine all libvirt VMs +sudo virsh list --all | awk 'NR>2 {print $2}' | while read vm; do + sudo virsh destroy "$vm" + sudo virsh undefine "$vm" --remove-all-storage +done + +echo "Removing all libvirt networks..." +# Destroy and undefine all libvirt networks +sudo virsh net-list --all | awk 'NR>2 {print $1}' | while read net; do + sudo virsh net-destroy "$net" + sudo virsh net-undefine "$net" +done + +echo "Removing all libvirt storage volumes..." +# Delete all storage volumes in the default pool +sudo virsh vol-list default | awk 'NR>2 {print $1}' | while read vol; do + sudo virsh vol-delete "$vol" default +done + +echo "Uninstalling libvirt and related packages..." +# Remove libvirt and related packages +sudo apt-get remove --purge -y libvirt-daemon-system libvirt-clients libvirt-dev qemu qemu-kvm bridge-utils virt-manager +sudo apt-get autoremove --purge -y + +echo "Deleting libvirt configs and storage directories..." +# Remove libvirt configuration and storage directories +sudo rm -rf /etc/libvirt +sudo rm -rf /var/lib/libvirt +sudo rm -rf /var/run/libvirt + +echo "Done! It is recommended to reboot your system." From 9a047d3789553a2e10f0b369b064bdcd48d7d011 Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 19 Nov 2025 12:39:45 +0100 Subject: [PATCH 2/3] vagrant and unsible base configs --- test_vm/README.MD | 23 +-------- test_vm/Vagrantfile | 18 +++++++ test_vm/provision.yml | 113 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 22 deletions(-) create mode 100644 test_vm/Vagrantfile create mode 100644 test_vm/provision.yml diff --git a/test_vm/README.MD b/test_vm/README.MD index 37be1891e..9a69f91e1 100644 --- a/test_vm/README.MD +++ b/test_vm/README.MD @@ -73,29 +73,8 @@ sudo virsh net-list --all You should see hostonly-noip with status active. -## 8. Create Your Vagrantfile - -In your project directory, create a file named Vagrantfile: - -```ruby -Vagrant.configure("2") do |config| - config.vm.box = "bento/ubuntu-24.04" - config.vm.hostname = "ubuntu-vm" - # First interface: NAT (default, for SSH) - # Second interface: direct to host via enbr99, no auto-IP - config.vm.network "private_network", - libvirt__network_name: "hostonly-noip", - auto_config: false, - mac: "52:54:00:12:34:56" - config.vm.provider :libvirt do |libvirt| - libvirt.memory = 2048 - libvirt.cpus = 2 - libvirt.graphics_type = "none" - end -end -``` -## 9. Start the Virtual Machine +## 8. Start the Virtual Machine ```bash diff --git a/test_vm/Vagrantfile b/test_vm/Vagrantfile new file mode 100644 index 000000000..da820d783 --- /dev/null +++ b/test_vm/Vagrantfile @@ -0,0 +1,18 @@ +Vagrant.configure("2") do |config| + config.vm.box = "bento/ubuntu-24.04" + config.vm.hostname = "ubuntu-vm" + config.vm.network "private_network", + libvirt__network_name: "hostonly-noip", + auto_config: false, + mac: "52:54:00:12:34:56" + config.vm.provider :libvirt do |libvirt| + libvirt.memory = 2048 + libvirt.cpus = 2 + libvirt.graphics_type = "none" + end + if ENV['USE_ANSIBLE'] == '1' + config.vm.provision "ansible" do |ansible| + ansible.playbook = "provision.yml" + end + end +end diff --git a/test_vm/provision.yml b/test_vm/provision.yml new file mode 100644 index 000000000..ed666c0f1 --- /dev/null +++ b/test_vm/provision.yml @@ -0,0 +1,113 @@ +--- +- hosts: all + become: true + tasks: + - name: Install DHCP client + apt: + name: isc-dhcp-client + state: present + update_cache: yes + + - name: Run dhclient on eth1 (in background) + shell: nohup dhclient eth1 & + async: 0 + poll: 0 + + - name: Install netstat for network troubleshooting + apt: + name: net-tools + state: present + update_cache: yes + + - name: Install BACnet dependencies + apt: + name: + - git + - gcc + - make + - bison + - flex + - libpcap-dev + state: present + update_cache: yes + tags: bacnet + + - name: Clone BACnet stack repository + git: + repo: https://github.com/bacnet-stack/bacnet-stack.git + dest: /opt/bacnet-stack + version: master + tags: bacnet + + - name: Build BACnet stack + make: + chdir: /opt/bacnet-stack + target: all + tags: bacnet + + - name: Copy bacserv-wrapper.sh + copy: + dest: /usr/local/bin/bacserv-wrapper.sh + mode: '0755' + content: | + #!/bin/bash + IFACE=eth1 + while true; do + IP=$(ip -4 addr show $IFACE | awk '/inet / {print $2}' | cut -d/ -f1) + if [ -n "$IP" ]; then + export BACNET_IP=$IP + exec /opt/bacnet-stack/bin/bacserv + fi + sleep 2 + done + tags: bacnet + + - name: Create systemd unit for bacserv + copy: + dest: /etc/systemd/system/bacserv.service + mode: '0644' + content: | + [Unit] + Description=BACnet Server (auto-restart on IP change) + After=network-online.target + Wants=network-online.target + + [Service] + Type=simple + ExecStart=/usr/local/bin/bacserv-wrapper.sh + Restart=always + RestartSec=5 + + [Install] + WantedBy=multi-user.target + tags: bacnet + + - name: Reload systemd and enable bacserv service + systemd: + name: bacserv + enabled: yes + state: restarted + daemon_reload: yes + tags: bacnet + + - name: Check if bacserv process is running + shell: pgrep -fl bacserv + register: bacserv_process + changed_when: false + tags: bacnet + + - name: Show information about bacserv process + debug: + var: bacserv_process.stdout_lines + tags: bacnet + + - name: Show last lines of bacserv log + shell: tail -n 20 /opt/bacnet-stack/bacserv.log + register: bacserv_log + changed_when: false + ignore_errors: true + + - name: Display bacserv log + debug: + var: bacserv_log.stdout_lines + tags: bacnet \ No newline at end of file From e55db38973eb7e90890eeb111e6b583c0707c62a Mon Sep 17 00:00:00 2001 From: Aliaksandr Nikitsin Date: Wed, 19 Nov 2025 22:11:32 +0100 Subject: [PATCH 3/3] local CA tutorial --- test_vm/how_to_create_local_ca.md | 123 ++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 test_vm/how_to_create_local_ca.md diff --git a/test_vm/how_to_create_local_ca.md b/test_vm/how_to_create_local_ca.md new file mode 100644 index 000000000..ff0684070 --- /dev/null +++ b/test_vm/how_to_create_local_ca.md @@ -0,0 +1,123 @@ +## 1. On the Host: Create Your Own CA + +```bash +mkdir ~/myCA +cd ~/myCA +openssl genrsa -out myorgca.key 4096 +openssl req -x509 -new -nodes -key myorgca.key -sha256 -days 1825 -out myorgca.pem -subj "/C=RU/O=MyOrganization/CN=MyOrgCA" +``` + +Here, O=MyOrganization is the Organization Name for your CA. + +## 2. On the VM: Create a Key, CSR, and Config with SAN and Organization Name + +### On the VM: + +Create a file named openssl_ip.cnf: + +```ini +[ req ] +default_bits = 2048 +prompt = no +default_md = sha256 +req_extensions = req_ext +distinguished_name = dn + +[ dn ] +C = RU +O = MyOrganization +CN = 10.10.10.14 + +[ req_ext ] +subjectAltName = @alt_names + +[ alt_names ] +IP.1 = 10.10.10.14 +``` + +Generate the key and CSR: + +```bash +openssl req -new -nodes -out nginx_ip.csr -newkey rsa:2048 -keyout nginx_ip.key -config openssl_ip.cnf +``` + +## 3. Copy the CSR and Config from the VM to the Host + +### On the host: + +```bash +scp vagrant@/home/vagrant/nginx_ip.csr ~/myCA/ +scp vagrant@:/home/vagrant/openssl_ip.cnf ~/myCA/ +``` + +## 4. On the Host: Sign the Certificate with Your CA + +```bash +cd ~/myCA +openssl x509 -req -in nginx_ip.csr -CA myorgca.pem -CAkey myorgca.key -CAcreateserial -out nginx_ip.crt -days 365 -sha256 -extfile openssl_ip.cnf -extensions req_ext +``` + +## 5. Copy the Certificate and CA Back to the VM + +### On the host: + +```bash +scp ~/myCA/nginx_ip.crt vagrant@:/home/vagrant/ +scp ~/myCA/nginx_ip.key vagrant@:/home/vagrant/ +scp ~/myCA/myorgca.pem vagrant@:/home/vagrant/ +``` + +## 6. On the VM: Install nginx and Configure the Certificate + +```bash +sudo apt update +sudo apt install nginx +sudo mv nginx_ip.crt /etc/ssl/certs/ +sudo mv nginx_ip.key /etc/ssl/private/ +sudo mv myorgca.pem /etc/ssl/certs/ +``` + +### Edit /etc/nginx/sites-available/default and add/replace the server block: + +```nginx + +server { + listen 443 ssl; + server_name 192.168.121.234; + + ssl_certificate /etc/ssl/certs/nginx_ip.crt; + ssl_certificate_key /etc/ssl/private/nginx_ip.key; + ssl_trusted_certificate /etc/ssl/certs/myorgca.pem; + + location / { + root /var/www/html; + index index.html index.htm; + } +} +``` + +### Test the config: + +```bash +sudo nginx -t +Restart nginx: +``` + +```bash +sudo systemctl restart nginx +``` + +## 7. On the host: Add the CA to the Trusted Store + +### On the host: + +```bash +cp ~/myCA/myorgca.pem ~/myCA/myorgca.crt +sudo cp ~/myCA/myorgca.crt /usr/local/share/ca-certificates/ +sudo update-ca-certificates +``` + +You should see: + + +Adding debian:myorgca.crt