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.
| Phase | Old panel | Matrix | Writes to the FortiGate |
|---|---|---|---|
| 1. Prepare | In use | Tenant, people, connector, firewall | Old panel |
| 2. Parallel run | In use | Reads only (switches off, guard not accepted) | Old panel |
| 3. Import | In use | Room rights and history imported | Old panel |
| 4. Cutover | Token made read-only | Switches on, guard accepted | Matrix |
| 5. Decommission | Stopped, archived, token deleted | In use | Matrix |
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
-
Tenant. A platform admin enables Entrosity Matrix for the school's organization on the Hub (Getting started).
-
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 panel Matrix role Administrator (active) Tenant admin Operator (active) Teacher Deactivated none 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.
-
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. -
Connector. Install the connector on a Windows computer of the administration network that reaches the FortiGate's management port (Connectors).
-
Firewall. Add the firewall in Matrix with the old panel's settings (its
FORTIGATE_*settings and the room naming rule of its documentation):Matrix field Value for the pilot school Address, Port, VDOM As in the old panel's FortiGate URL and VDOM ( root)Source interface or zone → Destination interface or zone Students→INTERNETPolicy name pattern Internet Access for (?P<room>(?:SB[0-9]+|FB|HAC)-[0-9]{3})Building names FB→ Фирма,HAC→ ЦВП (SB1, SB2, … show as Building 1, Building 2)Computer name suffix .coding.localExpected FortiOS version The version the FortiGate runs ( 7.4.11at the time of writing)Verify the FortiGate's TLS certificate On, 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 computers Off 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.
- 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.
- 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_policywarning 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). - 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.
- 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:
-
Finish unfinished operations in the old panel. Every unfinished address operation (Провери / повтори) must be completed or resolved there: they are not imported.
-
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 accprofileedit "room-panel-readonly"set netgrp readset fwgrp readnextendconfig system api-useredit "<old panel's API administrator>"set accprofile "room-panel-readonly"nextendConfirm 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.
-
Import once more (step 3) to bring over the last history and any rights changed since.
-
Accept the guard on the connector computer (
matrix-connector guard accept <firewall id>; for address changes alsoguard set <firewall id> --address-writes on) and switch Allow turning room policies on and off (and Allow changing and adding computers) on (Getting started). -
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.
-
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:
- 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..envandsecret.txtare not needed any more. - Stop the old panel:
docker compose downin its folder (without-vuntil the backup is verified;down -vdeletes its data volume). - Delete the old panel's API administrator on the FortiGate (which
revokes its token), and then securely delete
secret.txt. - Retire the address: remove the
network.coding.localhost 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 tohttps://hub.entrosity.com/matrix. Its certificate does not need renewing. - Remove the old panel's firewall rules and port openings on its host that are no longer needed.