Skip to main content

Moving from the old panel

Entrosity Matrix grew out of a single-school panel for managing room policies, which the pilot school runs at https://network.coding.local (a Django application with its own SQLite database, its own FortiGate API token, and the roles administrator and operator with allowed rooms). Matrix does the same inside Entrosity Hub, with the same checks on the FortiGate. This page moves the school over without a moment where two systems write to the firewall, and without losing the teachers' room rights or the history.

PhaseOld panelMatrixWrites to the FortiGate
1. PrepareIn useTenant, people, connector, firewallOld panel
2. Parallel runIn useReads only (switches off, guard not accepted)Old panel
3. ImportIn useRoom rights and history importedOld panel
4. CutoverToken made read-onlySwitches on, guard acceptedMatrix
5. DecommissionStopped, archived, token deletedIn useMatrix
Never let both write

The old panel and Matrix each lock a policy only for themselves; neither protects against changes made by the other. Until the cutover, Matrix must not be able to write (both switches off and the guard not accepted); after it, the old panel must not (its token read-only).

1. Prepare​

  1. Tenant. A platform admin enables Entrosity Matrix for the school's organization on the Hub (Getting started).

  2. People. Every user of the old panel who keeps working needs an Entrosity Hub account (their e-mail address) in the organization, with a Matrix role:

    Old panelMatrix role
    Administrator (active)Tenant admin
    Operator (active)Teacher
    Deactivatednone

    Accounts are matched by e-mail in step 3, and room rights are only imported for people who already have the Teacher role in Matrix.

  3. FortiGate API administrator for Matrix. Create a new REST API administrator for Matrix (do not reuse the old panel's): minimal profile, VDOM root, and the connector computer as its only trusted host (Setting up the FortiGate). The old panel's administrator trusts the old panel's host; the new one trusts the connector's.

  4. Connector. Install the connector on a Windows computer of the administration network that reaches the FortiGate's management port (Connectors).

  5. Firewall. Add the firewall in Matrix with the old panel's settings (its FORTIGATE_* settings and the room naming rule of its documentation):

    Matrix fieldValue for the pilot school
    Address, Port, VDOMAs in the old panel's FortiGate URL and VDOM (root)
    Source interface or zone → Destination interface or zoneStudents → INTERNET
    Policy name patternInternet Access for (?P<room>(?:SB[0-9]+|FB|HAC)-[0-9]{3})
    Building namesFB → Фирма, HAC → ЦВП (SB1, SB2, … show as Building 1, Building 2)
    Computer name suffix.coding.local
    Expected FortiOS versionThe version the FortiGate runs (7.4.11 at the time of writing)
    Verify the FortiGate's TLS certificateOn, with the FortiGate's CA. The old panel runs with verification off at the owner's request; switch it off in Matrix only if the CA is still not available.
    Allow turning room policies on and off, Allow changing and adding computersOff

    Store the new administrator's token (Firewalls → API token).

2. Parallel run (read-only)​

Leave both switches off and do not run guard accept on the connector computer: Matrix then only reads. Teachers keep using the old panel.

  1. Run Check on the firewall. Compare with the old panel: the same rooms (SB, FB and HAC), the same address groups and number of computers. Use the Pattern tester with Use names from the last check to make sure no other policy matches.
  2. Read the warnings. The old panel's documentation already records that the general enabled ACCEPT policy Internet Access for Students comes after the room policies and covers all their computers: expect a later_accept_policy warning for every room until that is fixed on the FortiGate. Until then, a disabled room is not proof that its internet is off, in either system (What switching a room off means).
  3. Watch Rooms during a few school days: when a teacher switches a room in the old panel, Matrix shows the new state within the report interval.
  4. Let administrators and a few teachers sign in and look around (teachers see no rooms until their rights are imported, and switch them only after the cutover).

3. Import rights and history​

matrix-server import-stop-internet brings the old panel's data into the tenant and firewall:

  • each active teacher's allowed rooms become Matrix room rights on the firewall (recorded as a Room rights entry imported from stop-internet);
  • the history of room switches, IP changes and new computers becomes Matrix history with its original time, user, IP and result, marked Imported from the old panel (source stop-internet). Sign-ins, account changes and refused-access entries are not imported; entries still pending in the old panel are imported as Unconfirmed.

Running it again adds only what is new, so it can be run during the parallel run and once more at the cutover.

Export from the old panel (read-only)​

Nothing in the old panel is changed: the export opens its database read-only (mode=ro) inside the running container and writes to standard output. Save this as export_stop_internet.py on the old panel's host, next to its compose.yaml:

# Read-only export of the old room panel for import-stop-internet.
import json, sqlite3, sys

db = sqlite3.connect("file:/app/data/db.sqlite3?mode=ro", uri=True)
db.row_factory = sqlite3.Row

def utc(v): # Django stores UTC as "YYYY-MM-DD HH:MM:SS[.ffffff]"
return v.replace(" ", "T") + ("" if v.endswith("Z") or "+" in v else "Z")

users = [
{"username": r["username"], "role": r["role"], "is_active": bool(r["is_active"]),
"allowed_rooms": json.loads(r["allowed_rooms"] or "[]")}
for r in db.execute("SELECT username, role, is_active, allowed_rooms FROM panel_user ORDER BY username")
]
history = [
{"id": r["id"], "created_at": utc(r["created_at"]), "username": r["username"], "ip": r["ip"] or "",
"kind": r["kind"], "room": r["room"], "policy_id": r["policy_id"], "policy_name": r["policy_name"],
"previous": r["previous"], "desired": r["desired"], "result": r["result"], "detail": r["detail"],
"object_name": r["object_name"], "group_name": r["group_name"],
"operation_id": r["operation_id"] or "", "metadata": json.loads(r["metadata"] or "{}")}
for r in db.execute("SELECT * FROM panel_auditevent ORDER BY id")
]
json.dump({"users": users, "history": history}, sys.stdout, ensure_ascii=False, indent=1)

Run it, and list the user names for users.csv:

docker compose exec -T app python - < export_stop_internet.py > export.json
python3 -c 'import json; [print(u["username"] + ",") for u in json.load(open("export.json"))["users"] if u["is_active"]]' > users.csv

Complete users.csv by hand: one line per active user, the old panel's user name and the e-mail address of the same person's Hub account, with an optional header line:

username,email
teacher-sb1,[email protected]

export.json and users.csv contain user names, e-mail addresses and IP addresses: keep them with restricted access, move them to the Matrix server over SSH only, and delete them after the import.

Run the import​

On the Matrix server, as the server's operator, in the deploy directory (/opt/entrosity/deploy), with the two files in a folder import/ readable by the container. The tenant ID is in the Matrix address (/matrix/t/<tenant id>/), the firewall ID in its page's address (…/settings/firewalls/<firewall id>):

docker compose -f docker-compose.prod.yml --env-file .env --profile matrix run --rm --no-deps \
-v "$PWD/import:/import:ro" matrix \
import-stop-internet --tenant <tenant id> --firewall <firewall id> \
--users /import/users.csv --export /import/export.json

It prints a warning for each user it could not match (no Hub account with that e-mail in Matrix yet, or not an Teacher) and for rooms the firewall does not report, then a summary:

warning: user teacher-x ([email protected]) is not a Matrix user: rooms not granted
room grants added: 42 (users: 17); history imported: 3120, skipped: 850

Fix the warnings (invite the person, give them the Teacher role, wait a minute) and run it again; it only adds what is missing. Then check Room rights and History in Matrix.

4. Cutover​

Pick a time outside lessons. In this order:

  1. Finish unfinished operations in the old panel. Every unfinished address operation (Провери / повтори) must be completed or resolved there: they are not imported.

  2. Make the old panel read-only on the FortiGate. Give the old panel's REST API administrator a read-only profile, so its token can no longer write:

    config system accprofile
    edit "room-panel-readonly"
    set netgrp read
    set fwgrp read
    next
    end
    config system api-user
    edit "<old panel's API administrator>"
    set accprofile "room-panel-readonly"
    next
    end

    Confirm the change in the FortiGate's configuration (not by switching a room in the old panel). From now on any change attempted there is refused by the FortiGate.

  3. Import once more (step 3) to bring over the last history and any rights changed since.

  4. Accept the guard on the connector computer (matrix-connector guard accept <firewall id>; for address changes also guard set <firewall id> --address-writes on) and switch Allow turning room policies on and off (and Allow changing and adding computers) on (Getting started).

  5. Test on the test room agreed with the FortiGate's administrator: disable, check from a computer of the room, enable. For addresses, a test computer and group.

  6. Tell the teachers the new address, https://hub.entrosity.com/matrix, and that they sign in with their Hub account.

Rolling back during the first days: switch Matrix's two switches off and run matrix-connector guard reset <firewall id>, then give the old panel's API administrator its previous profile back. Never have both enabled.

5. Decommission​

After a few weeks with Matrix only:

  1. Last backup of the old panel as its documentation describes (stop the app, copy db.sqlite3), and keep it with restricted access for as long as the school keeps the history. .env and secret.txt are not needed any more.
  2. Stop the old panel: docker compose down in its folder (without -v until the backup is verified; down -v deletes its data volume).
  3. Delete the old panel's API administrator on the FortiGate (which revokes its token), and then securely delete secret.txt.
  4. Retire the address: remove the network.coding.local host from the web server's configuration (the HTTP → HTTPS redirect for that name installed with the old panel) and from internal DNS, or point it to a page that links to https://hub.entrosity.com/matrix. Its certificate does not need renewing.
  5. Remove the old panel's firewall rules and port openings on its host that are no longer needed.