diff --git a/app/network/forms.py b/app/network/forms.py
index b82c482ca76914485e3babed083b784ec8111d60..755626abac6176bf3de2b5024192968448dffe21 100644
--- a/app/network/forms.py
+++ b/app/network/forms.py
@@ -133,6 +133,7 @@ class InterfaceForm(CSEntryForm):
                     Unique(models.Interface),
                     starts_with_hostname],
         filters=[utils.lowercase_field])
+    random_mac = BooleanField('Random MAC', default=False)
     mac_address = StringField(
         'MAC',
         validators=[validators.Optional(),
diff --git a/app/network/views.py b/app/network/views.py
index 3f1fe565df4100017a824750e3e8a87fca398090..eefcadc79ac3fc566eadedf80f212125f9b7be56 100644
--- a/app/network/views.py
+++ b/app/network/views.py
@@ -32,15 +32,16 @@ def list_hosts():
 @bp.route('/hosts/create', methods=('GET', 'POST'))
 @login_groups_accepted('admin', 'create')
 def create_host():
+    kwargs = {'random_mac': True}
     # Try to get the network_id from the session
     # to pre-fill the form with the same network
     try:
         network_id = session['network_id']
     except KeyError:
-        # No need to pass request.form when no extra keywords are given
-        form = HostInterfaceForm()
+        pass
     else:
-        form = HostInterfaceForm(request.form, network_id=network_id)
+        kwargs['network_id'] = network_id
+    form = HostInterfaceForm(request.form, **kwargs)
     # Remove the host_id field inherited from the InterfaceForm
     # It's not used in this form
     del form.host_id
@@ -112,7 +113,9 @@ def edit_host(name):
 @login_groups_accepted('admin', 'create')
 def create_interface(hostname):
     host = models.Host.query.filter_by(name=hostname).first_or_404()
-    form = InterfaceForm(request.form, host_id=host.id, interface_name=host.name)
+    random_mac = host.type == 'Virtual'
+    form = InterfaceForm(request.form, host_id=host.id, interface_name=host.name,
+                         random_mac=random_mac)
     if form.validate_on_submit():
         # The total number of tags will always be quite small
         # It's more efficient to retrieve all of them in one query
@@ -153,6 +156,8 @@ def edit_interface(name):
                          interface_name=interface.name,
                          mac_address=mac_address,
                          cnames_string=cnames_string)
+    # Remove the random_mac field (not used when editing)
+    del form.random_mac
     ips = [interface.ip]
     ips.extend([str(address) for address in interface.network.available_ips()])
     form.ip.choices = utils.get_choices(ips)
@@ -441,3 +446,10 @@ def retrieve_domains():
     data = [(domain.name,)
             for domain in models.Domain.query.all()]
     return jsonify(data=data)
+
+
+@bp.route('/_generate_random_mac')
+@login_required
+def generate_random_mac():
+    data = {'mac': utils.random_mac()}
+    return jsonify(data=data)
diff --git a/app/settings.py b/app/settings.py
index 2812fae4f15202dfc6d001e7546c1ac7b9d964ce..9ec9c7da2deab19c61c98c60ce8dea835e191766 100644
--- a/app/settings.py
+++ b/app/settings.py
@@ -58,3 +58,7 @@ NETWORK_DEFAULT_PREFIX = 24
 # (waiting for a real label to be assigned)
 # WARNING: This is defined here as a global settings but should not be changed!
 TEMPORARY_ICS_ID = 'ZZ'
+
+# CSENTRY MAC organizationally unique identifier
+# This is a locally administered address
+MAC_OUI = '02:42:42'
diff --git a/app/static/js/hosts.js b/app/static/js/hosts.js
index 4c4b976d8bc9cd9e6d5141c2dcb81bf19ff30e93..6a4ec41bd890a49467488aab9d9014e9b9f9f7e3 100644
--- a/app/static/js/hosts.js
+++ b/app/static/js/hosts.js
@@ -16,6 +16,23 @@ $(document).ready(function() {
     );
   }
 
+  // If random_mac is checked, generate a random address
+  // Empty the field otherwise
+  function fill_mac_address() {
+    if( $("#random_mac").prop("checked") ) {
+      $.getJSON(
+        $SCRIPT_ROOT + "/network/_generate_random_mac",
+        function(json) {
+          $("#mac_address").val(json.data.mac);
+          $("#mac_address").prop("readonly", true);
+        }
+      );
+    } else {
+      $("#mac_address").val("");
+      $("#mac_address").prop("readonly", false);
+    }
+  }
+
   // Populate IP select field on first page load for:
   // - register new host
   // - add interface
@@ -32,13 +49,16 @@ $(document).ready(function() {
 
   // Enable / disable item_id field depending on type
   // Item can only be assigned for physical hosts
+  // And check / uncheck random_mac checkbox
   $("#type").on('change', function() {
     var host_type = $(this).val();
     if( host_type == "Physical" ) {
       $("#item_id").prop("disabled", false);
+      $("#random_mac").prop("checked", false).change();
     } else {
       $("#item_id").val("");
       $("#item_id").prop("disabled", true);
+      $("#random_mac").prop("checked", true).change();
     }
   });
 
@@ -48,6 +68,16 @@ $(document).ready(function() {
     $("#interface_name").val(hostname);
   });
 
+  // Fill MAC address on page load
+  if( $("#random_mac").length ) {
+    fill_mac_address();
+  }
+
+  // Fill or clear MAC address depending on random_mac checkbox
+  $("#random_mac").on('change', function() {
+    fill_mac_address();
+  });
+
   // Force the first interface to have the hostname as name
   // This only applies to the hostForm (not when editing or adding interfaces)
   $("#hostForm input[name=interface_name]").prop("readonly", true);
diff --git a/app/templates/network/create_host.html b/app/templates/network/create_host.html
index 775a29b06d3783807a5ab74e33eebfaf07c33b61..1a2f35fce0c32fb6b0d072006604fe561a5483b3 100644
--- a/app/templates/network/create_host.html
+++ b/app/templates/network/create_host.html
@@ -13,6 +13,7 @@
     {{ render_field(form.network_id) }}
     {{ render_field(form.ip) }}
     {{ render_field(form.interface_name, class_="text-lowercase") }}
+    {{ render_field(form.random_mac) }}
     {{ render_field(form.mac_address) }}
     {{ render_field(form.cnames_string) }}
     {{ render_field(form.tags) }}
diff --git a/app/templates/network/create_interface.html b/app/templates/network/create_interface.html
index 2da2cdb5da5d10094ce6216eb0390ebeae1be784..85ed4ba24e7937f5a10f2c3d50932b143673108a 100644
--- a/app/templates/network/create_interface.html
+++ b/app/templates/network/create_interface.html
@@ -23,6 +23,7 @@
     {{ render_field(form.interface_name, class_="text-lowercase") }}
     {{ render_field(form.network_id) }}
     {{ render_field(form.ip) }}
+    {{ render_field(form.random_mac) }}
     {{ render_field(form.mac_address) }}
     {{ render_field(form.cnames_string) }}
     {{ render_field(form.tags) }}
diff --git a/app/utils.py b/app/utils.py
index ee68efe225dbaf374d35eb21056db9843229a9f1..5c84920ee30722a282387d03fd6f60bdb6bdd869 100644
--- a/app/utils.py
+++ b/app/utils.py
@@ -12,8 +12,10 @@ This module implements utility functions.
 import base64
 import datetime
 import io
+import random
 import sqlalchemy as sa
 import dateutil.parser
+from flask import current_app
 from flask.globals import _app_ctx_stack, _request_ctx_stack
 from flask_login import current_user
 from flask_jwt_extended import get_current_user
@@ -179,3 +181,12 @@ def parse_to_utc(string):
     # Convert to UTC and remove timezone
     d = d.astimezone(datetime.timezone.utc)
     return d.replace(tzinfo=None)
+
+
+def random_mac():
+    """Return a random MAC address"""
+    octets = [random.randint(0x00, 0xFF),
+              random.randint(0x00, 0xFF),
+              random.randint(0x00, 0xFF)]
+    octets = [f'{nb:02x}' for nb in octets]
+    return ':'.join((current_app.config['MAC_OUI'], *octets))
diff --git a/tests/functional/test_web.py b/tests/functional/test_web.py
index 644b41d6627b102a46f24b2e75a29c63e189cc43..6da1cb8c9329244559102e64724f7eaa6c5389d2 100644
--- a/tests/functional/test_web.py
+++ b/tests/functional/test_web.py
@@ -11,6 +11,7 @@ This module defines basic web tests.
 """
 import json
 import pytest
+import re
 
 
 def get(client, url):
@@ -81,3 +82,10 @@ def test_retrieve_items(logged_client, item_factory):
     items = response.json['data']
     assert set(serial_numbers) == set(item[4] for item in items)
     assert len(items[0]) == 11
+
+
+def test_generate_random_mac(logged_client):
+    response = get(logged_client, '/network/_generate_random_mac')
+    mac = response.json['data']['mac']
+    assert re.match('^(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$', mac) is not None
+    assert mac.startswith('02:42:42')