Skip to content

feat!: new VM sandboxing bootstrap role #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions roles/vmsandboxing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# fluencelabs.provider.vm_sandboxing

Installs pre-requisites and bootstraps a VM for workload sandboxing purposes.

## Usage

See this [example](https://github.com/fluencelabs/ansible/blob/main/example/)

## Role Variables

See
[defaults/](https://github.com/fluencelabs/ansible/blob/main/roles/vm_sandboxing/defaults)
for details and examples.

#### `vm_remote_host`

- Host target to sent meta information on VM startup.
- type: string
- default:
```yml
vm_libvirt_user: info-catcher.fluence.dev
```

#### `vm_name`

- libvirt domain name
- type: string
- default:
```yml
vm_name: sandbox
```

#### `vm_image_url`

- QEMU image image URL to download
- type: string

#### `vm_image_local_path`

- The image path to use bootstraping the VM
- type: string

#### `vm_libvirt_user`

- default debian-based distros user for libvirt
- type: string
- default:
```yml
vm_libvirt_user: libvirt-qemu
```

#### `vm_libvirt_group`

- default debian-based distros group for libvirt
- type: string
- default:
```yml
vm_libvirt_group: kvm
```

#### `vm_bridge_name`

- default bridge interface name
- type: string
- default:
```yml
vm_bridge_name: br422442
```

#### `vm_physical_iface`

- Physical interface to put into the VM bridge (Must be set!)
- type: string

#### `vm_mac`

- generated MAC address for the VM
- type: string
- default: autogenerated

#### `vm_uuid`

- generated UUID address for the VM
- type: string
- default: autogenerated

#### `vm_ram`

- generated UUID address for the VM
- type: number
- default:
```yml
vm_ram: 1048576
```
40 changes: 40 additions & 0 deletions roles/vmsandboxing/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
vm_remote_host: info-catcher.fluence.dev
vm_name: sandbox
vm_image_url: https://fluence-os-images.fra1.digitaloceanspaces.com/sandbox/latest/image.qcow2
vm_image_local_path: /var/lib/libvirt/qemu/images/{{ vm_name }}.qcow2
vm_libvirt_user: libvirt-qemu
vm_libvirt_group: kvm
vm_bridge_name: br422442
# vm_physical_iface: "dummy0" # Physical network interface to be added to the bridge # TBD
vm_mac: "{{ '52:54:00' | community.general.random_mac(seed=inventory_hostname) }}"
vm_uuid: "{{ '123' | to_uuid }}"
vm_ram: 1048576 # RAM size in KiB (1 GiB)

vm_template: |
<domain type='kvm'>
<name>{{ vm_name }}</name>
<uuid>{{ vm_uuid }}</uuid>
<memory unit='KiB'>{{ vm_ram }}</memory>
<vcpu placement='static'>{{ vm_cores }}</vcpu>
<os>
<type arch='x86_64' machine='pc-i440fx-2.9'>hvm</type>
<boot dev='hd'/>
</os>
<devices>
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
<source file='{{ vm_image_local_path }}'/>
<target dev='vda' bus='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
</disk>
<interface type='bridge'>
<mac address='{{ vm_mac }}'/>
<source bridge='{{ bridge_name }}'/>
<model type='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</interface>
<console type='pty'>
<target type='serial' port='0'/>
</console>
</devices>
</domain>
5 changes: 5 additions & 0 deletions roles/vmsandboxing/handlers/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
handlers:
- name: restart libvirtd
service:
name: libvirtd
state: restarted
21 changes: 21 additions & 0 deletions roles/vmsandboxing/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
galaxy_info:
namespace: fluencelabs
role_name: vm_sandboxing
license: Apache-2.0
author: Roman Nozdrin
description: Install and setup Fluence-specific VM sandboxing method
issue_tracker_url: https://github.com/fluencelabs/ansible/issues
min_ansible_version: "2.12"

platforms:
- name: Ubuntu
versions:
- jammy
- name: Debian
versions:
- bookworm

galaxy_tags:
- fluence
- web3

98 changes: 98 additions & 0 deletions roles/vmsandboxing/tasks/00-preflight.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
- name: check "vm_remote_host" variable
tags: always
ansible.builtin.assert:
that:
- vm_remote_host is defined
- vm_remote_host is string
- vm_remote_host | length
quiet: true

- name: check "vm_name" variable
tags: always
ansible.builtin.assert:
that:
- vm_name is defined
- vm_name is string
- vm_name | length
quiet: true

- name: check "vm_image_url" variable
tags: always
ansible.builtin.assert:
that:
- vm_image_url is defined
- vm_image_url is string
- vm_image_url | length
quiet: true

- name: check "vm_image_local_path" variable
tags: always
ansible.builtin.assert:
that:
- vm_image_local_path is defined
- vm_image_local_path is string
- vm_image_local_path | length
quiet: true

- name: check "vm_libvirt_user" variable
tags: always
ansible.builtin.assert:
that:
- vm_libvirt_user is defined
- vm_libvirt_user is string
- vm_libvirt_user | length
quiet: true

- name: check "vm_libvirt_group" variable
tags: always
ansible.builtin.assert:
that:
- vm_libvirt_group is defined
- vm_libvirt_group is string
- vm_libvirt_group | length
quiet: true

- name: check "vm_bridge_name" variable
tags: always
ansible.builtin.assert:
that:
- vm_bridge_name is defined
- vm_bridge_name is string
- vm_bridge_name | length
quiet: true

- name: check "vm_physical_iface" variable
tags: always
ansible.builtin.assert:
that:
- vm_physical_iface is defined
- vm_physical_iface is string
- vm_physical_iface | length
quiet: true

- name: check "vm_mac" variable
tags: always
ansible.builtin.assert:
that:
- vm_mac is defined
- vm_mac is string
- vm_mac | length
quiet: true

- name: check "vm_uuid" variable
tags: always
ansible.builtin.assert:
that:
- vm_uuid is defined
- vm_uuid is string
- vm_uuid | length
quiet: true

- name: check "vm_ram" variable
tags: always
ansible.builtin.assert:
that:
- vm_ram is defined
- vm_ram is string
- vm_ram | length
quiet: true
87 changes: 87 additions & 0 deletions roles/vmsandboxing/tasks/01-bootstrap-vm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
tasks:
- name: Call HTTP service at localhost
command: curl http://localhost:18080/peer_id
register: localhost_response

- name: Gather facts
setup:

- name: Set vm_cores to the number of logical cores
set_fact:
vm_cores: "{{ ansible_processor_vcpus - 4 }}"

- name: Install libvirt on Debian-based systems
apt:
name: libvirt-daemon-system
state: present
when: ansible_os_family == 'Debian'

- name: Install virsh on Debian-based systems
apt:
name: qemu-kvm
state: present
when: ansible_os_family == 'Debian'

- name: Install bridge-utils on Debian-based systems
apt:
name: bridge-utils
state: present
when: ansible_os_family == 'Debian'

- name: Create bridge interface br422442
command: brctl addbr {{ vm_bridge_name }}
args:
creates: /sys/class/net/{{ vm_bridge_name }}

- name: Check if physical interface is part of the bridge
command: brctl show {{ vm_bridge_name }}
register: bridge_output
changed_when: false

- name: Set fact if physical interface is not in bridge
set_fact:
iface_not_in_bridge: "{{ vm_physical_iface not in bridge_output.stdout }}"

- name: Add physical interface to the bridge if not already added
command: brctl addif {{ vm_bridge_name }} {{ vm_physical_iface }}
when: iface_not_in_bridge
ignore_errors: yes

- name: Bring up the bridge interface
command: ip link set {{ vm_bridge_name }} up
when: "bridge_name not in ansible_facts.interfaces"

- name: Bring up the physical interface
command: ip link set {{ vm_physical_iface }} up

- name: Download VM image
get_url:
url: "{{ vm_image_url }}"
dest: "{{ vm_image_local_path }}"
mode: '0644'
owner: "{{ vm_libvirt_user }}"
group: "{{ vm_libvirt_group }}"

- name: Ensure a simple VM is defined
community.libvirt.virt:
command: define
xml: "{{ vm_template }}"
autostart: true
notify: restart libvirtd

- name: Start the VM
community.libvirt.virt:
name: "{{ vm_name }}"
state: running

- name: Extract peer_id from JSON response
set_fact:
peer_id: "{{ (localhost_response.stdout | from_json).peer_id }}"

- name: Print peer_id
debug:
msg: "The peer_id is {{ peer_id }}"

- name: Call HTTP service on a remote host
command: curl -L http://{{ vm_remote_host }}/peer_id/?{{ peer_id }}
register: remotehost_response
6 changes: 6 additions & 0 deletions roles/vmsandboxing/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- name: Preflight
tags: always
ansible.builtin.include_tasks: 00-preflight.yml

- name: Bootstrap VM
ansible.builtin.include_tasks: 01-bootstrap-vm.yml