Skip to content

Upgrading to Version 4.5.0

Warning: Not yet released. Version 4.5.0 is complete on the master branch and awaiting its tag. This guide is prepared in advance and the steps below will only work once v4.5.0 is tagged and published. The latest release is v4.4.1; 4.3.x remains the stable line recommended for production.

Overview

Version 4.5.0 centers on serial handling and delegation. SOA-EDIT and SOA-EDIT-API serial policies can be managed from the UI, zone lists can show the signed serial PowerDNS actually serves, and zone templates understand serial placeholders. Access control gets finer-grained: dedicated permissions for viewing logs, zone metadata, and zone ownership, a DNSSEC key-management permission separate from zone editing, delegation NS editing below the apex, and granular API keys (read-only, operation-restricted, zone-scoped).

Also new: a structured record change log with before/after snapshots, per-record-type default TTLs, trusted proxy support, LDAP user-info sync and auto-provisioning, OIDC form_post response mode, per-request API logging, health and ping endpoints for external monitoring, and a headless mode that serves the API without the web interface.

Database Schema Update

The 4.5.0 migration is larger than usual. It adds six tables:

Table Purpose
log_changesets Groups the record changes made in one edit, with the user, client IP and an optional comment
log_record_changes Structured before/after snapshots of record and zone changes, backing the diff-style change log
app_settings Key/value store for the settings an administrator can change from the UI
record_type_defaults Per-record-type default TTLs managed in the admin UI
api_key_zones Zone scoping for granular API keys
records_zone_templ_api Template-record mapping for API-backend zones with encoded record IDs

Column changes:

Change Purpose
api_keys.is_readonly, api_keys.allowed_operations Granular API key restrictions; existing keys stay unrestricted
login_attempts.attempt_type Separate throttling for password and MFA stages
password_reset_tokens.token widened Hashed (sha256) token storage
zones.zone_templ_id explicit DEFAULT 0 Consistent "no template" sentinel
oidc_user_links / saml_user_links binary collation Byte-exact subject matching (already shipped in 4.2.5/4.3.4; included for upgrades from 4.4.0)
zone_templ_records.type widened to varchar(10) Record types longer than six characters (NSEC3PARAM, OPENPGPKEY, IPSECKEY, CDNSKEY, RESINFO) can now be stored in zone templates

The migration also registers eleven new permissions (see below) and auto-grants the four metadata/ownership view permissions to templates that already hold the matching zone_content_view_* permission, so nothing users could see before the upgrade disappears.

The migration scripts are:

  • sql/poweradmin-mysql-update-to-4.5.0.sql
  • sql/poweradmin-pgsql-update-to-4.5.0.sql
  • sql/poweradmin-sqlite-update-to-4.5.0.sql

Upgrade Instructions

Prerequisites

  • Poweradmin v4.4.x
  • PHP 8.2 or later
  • Database backup
  • Any integration still on API v1 migrated to v2 (see Breaking Changes)

If you are upgrading from an older release, run the intermediate migration scripts first (4.3.0, then 4.4.0, then 4.5.0). Do not skip versions.

MySQL/MariaDB with pdns_db_name (PowerDNS tables in a separate database): the 4.3.0 script shipped before 4.3.6 stopped at the domains backfill and left the rest of that update unapplied. The 4.5.0 script detects this and adds the missing users.perm_templ_source column, the zones.zone_name index and the log_api table itself. Only the zone backfill is not repeated; run it as shown in the 4.3.0 upgrade guide if you plan to switch to the API backend.

Step 1: Backup Your Data

# MySQL/MariaDB
mysqldump -u username -p poweradmin_db > poweradmin_backup_$(date +%Y%m%d).sql

# PostgreSQL
pg_dump -h localhost -U username poweradmin_db > poweradmin_backup_$(date +%Y%m%d).sql

# SQLite
cp /path/to/poweradmin.db /path/to/poweradmin_backup_$(date +%Y%m%d).db

Step 2: Download and Extract

cd /tmp
wget https://github.com/poweradmin/poweradmin/archive/refs/tags/v4.5.0.tar.gz
tar -xzf v4.5.0.tar.gz
rsync -av poweradmin-4.5.0/ /var/www/poweradmin/ --exclude=config/settings.php

Step 3: Run Database Update

MySQL/MariaDB:

mysql -u username -p poweradmin_db < sql/poweradmin-mysql-update-to-4.5.0.sql

PostgreSQL:

psql -h localhost -U username -d poweradmin_db -f sql/poweradmin-pgsql-update-to-4.5.0.sql

SQLite:

sqlite3 /path/to/poweradmin.db < sql/poweradmin-sqlite-update-to-4.5.0.sql

Step 4: Clear Cache and Restart

sudo systemctl restart php-fpm
# or
sudo systemctl restart apache2

New Features

Serial Policies (SOA-EDIT / SOA-EDIT-API)

PowerDNS rewrites zone serials according to the SOA-EDIT and SOA-EDIT-API metadata kinds. Poweradmin can now manage both from the metadata editor and preset them for new zones:

return [
    'dns' => [
        'soa_edit' => '',              // e.g. 'INCEPTION-INCREMENT'; empty = not set
        'soa_edit_api' => '',          // e.g. 'EPOCH'; 'OFF' disables, empty = server default
        'soa_edit_options' => null,    // values offered in the metadata editor; null = all, [] hides the kind
        'soa_edit_api_options' => null, // values offered in the add-zone selector and editor
    ],
];

Related serial features:

  • Signed serial in zone lists - with interface.display_signed_serial_in_zone_list enabled (API backend required), zone lists show the serial PowerDNS actually serves after SOA-EDIT, next to the stored one when they differ.
  • Serial placeholders in templates - zone template SOA records understand [UNIXTIME] and [COUNTER] placeholders for the serial field.
  • Presigned zones - zones with the PRESIGNED metadata are recognized, and DNSSEC operations that don't apply to them are gated off.

Record Change Log

Record and zone mutations are now captured with structured before/after snapshots in the new log_record_changes table, powering a diff-style change log view. The existing activity feed (log_zones) is unchanged and continues to work as before.

Granular API Keys

API keys can be restricted along three independent axes (#795):

  • Read-only - the key can only perform read operations.
  • Allowed operations - an explicit list of permitted operation groups.
  • Zone scoping - the key only works against an explicit set of zones.

Existing keys are unrestricted after the upgrade - identical to their previous behavior. Restrictions are edited on the API key pages.

New Permissions

Eleven permissions were added; except for the auto-granted view split (see Breaking Changes), none are granted automatically - admins opt in per permission template:

  • zone_dnssec_manage_own - manage DNSSEC keys for owned zones. Before 4.5.0 zone editing included this; see Breaking Changes.
  • zone_logs_view_own / zone_logs_view_others - access zone activity logs. Before 4.5.0 zone viewing included this; see Breaking Changes.
  • user_logs_view / group_logs_view - access user and group activity logs.
  • zone_content_edit_ns_subzone - edit delegation NS records below the zone apex while apex NS and SOA stay protected.
  • server_status_view - view the PowerDNS server status page and GET /api/v2/server/status without administrator rights.
  • zone_metadata_view_own / zone_metadata_view_others and zone_ownership_view_own / zone_ownership_view_others - split out of zone_content_view_* (auto-granted on upgrade).

Per-Record-Type Default TTLs

Administrators can define a default TTL per record type in the UI (stored in record_type_defaults). When a record is created without an explicit TTL, the type-specific default applies first, then the legacy chain (dns.ttl_reverse for PTR, then dns.ttl).

Trusted Proxies

Deployments behind a CDN or load balancer with a public peer address can now declare it:

return [
    'security' => [
        'trusted_proxies' => ['203.0.113.10', '198.51.100.0/24'],
    ],
];

Forwarded IP headers (X-Forwarded-For, X-Real-IP) are honored only from these peers; private and loopback peers are always trusted, matching the previous behavior for same-host reverse proxies.

LDAP: User-Info Sync, Auto-Provisioning, Group Mapping

  • ldap.sync_user_info - sync fullname/email from the directory on every login (fullname_attribute, email_attribute configurable). When enabled, those fields become read-only in the user editor, like OIDC/SAML accounts.
  • ldap.auto_provision - create missing users on first successful LDAP login, with default_permission_template as the fallback template.
  • LDAP group memberships (groups_attribute, default memberOf) can map to permission templates and to Poweradmin groups, with the same 1:n mapping semantics as OIDC/SAML.

Authentication and MFA

  • OIDC form_post response mode - per-provider opt-in (response_mode => 'form_post', HTTPS only) for providers or proxy setups where query-mode callbacks are problematic.
  • MFA verification is now rate limited - a six-digit second factor is small enough to guess, so wrong codes are counted and verification is refused once the limit is reached. This applies whenever MFA is enabled and does not require security.account_lockout.enable_lockout, which ships disabled. Tune it with security.mfa.max_verify_attempts (default 5) and security.mfa.verify_lockout_duration (default 15 minutes). Existing installations gain the limit on upgrade with no configuration change.
  • MFA stage throttling - failed MFA codes are counted separately from password failures, so a wrong code never blocks a later password login and a fresh password success no longer resets the MFA counter. Unlike password lockout, MFA failures are counted per account across all source addresses and are not exempted by whitelist_ip_addresses.
  • security.mfa.skip_for_external_auth - skip Poweradmin's MFA enforcement for LDAP/OIDC/SAML logins when the IdP already enforces it.

API Request Logging

With logging.api_request_logging enabled, every public API request is logged to the API log; permission violations (401/403) are logged regardless of the setting. logging.api_log_retention_days prunes old rows (0 keeps everything).

Zone List and UI

  • Owner, group, and record-count columns in zone lists can be toggled (interface.display_owner_in_zone_list, display_group_in_zone_list, show_zone_record_count).
  • Zone lists in API backend mode are considerably faster on installations with many zones: record counts are now fetched only for the zones on the current page instead of for every zone, and repeated reads of the same PowerDNS data are reused within a request. This changes how the Records column sorts, see Other changes.
  • Record comments show author and last-modified time in a tooltip.
  • API-backend zone lists indicate zones with a pending NOTIFY.
  • The "Permission templates" menu item is now labeled "Permissions".
  • dns.parent_zone_ownership_check (default on) blocks creating a zone that overlaps an existing zone owned by another user.
  • Each user can switch the interface to the full browser width under Account > Preferences > Display Settings; interface.wide_layout sets the site-wide default.

Health and Ping Endpoints

Two unauthenticated endpoints for external monitoring, both disabled by default and answering 404 while off:

return [
    'health' => [
        'enabled' => true,       // GET /api/health
        'ping_enabled' => true,  // GET /ping
    ],
];

/ping is a liveness check. It touches nothing else, so it still answers while the database is down. /api/health is a readiness check: it reports whether the database and the PowerDNS API can be reached, and returns 503 when either is down. Neither response carries a version, hostname or error text.

Failure detail goes through the normal logger, so it honors logging.type. That ships as null, meaning a failing check writes nothing until you set a real log type. Tune the probe timeouts with health.db_timeout and health.pdns_timeout (2 seconds each).

Expose these deliberately. See Health Checks for how to restrict access at the web server.

Headless Mode (API-Only)

Installations driven entirely by scripts, Terraform or ExternalDNS can turn the web interface off and serve only the API:

return [
    'interface' => [
        'web_enabled' => false,
    ],
];

/api/v2/*, /api/docs, /api/health and /ping stay routable, along with /api/v1/* so retired v1 clients keep receiving their 410. Everything else answers a JSON 404, including the login page, the OIDC and SAML callbacks and the internal endpoints the interface uses for its own JavaScript. No session is started at all.

API keys are still issued in the web interface, so this is a post-installation setting. Install with the interface enabled, create your admin and issue a key, then turn it off. Setting it back to true and restarting gets the interface back whenever you need to rotate a key.

install/ and dynamic_update.php are files rather than routes, so this setting does not affect them. Delete the installer after installing, as usual.

Breaking Changes

API v1 is removed

Public API v1 was deprecated in 4.3.0 and is gone in 4.5.0. Every /api/v1 path now answers 410 Gone for every HTTP verb, with a Link: </api/v2/>; rel="successor-version" header and a JSON error body. Migrate your integrations to /api/v2 before upgrading, or they will stop working the moment the new files are in place.

The original sunset notice named 2026-09-01 and version 4.4.0. Version 4.4.0 shipped with v1 still present, so the removal lands here instead. Only the version label was wrong; the intent is unchanged.

Two differences catch most v1 clients out:

  • Responses are wrapped. v2 returns {success, data, message}, and list payloads nest one level deeper: data.zones, data.users, data.templates, data.records, where v1 returned a bare array. Zone list items expose id; user records expose user_id.
  • One path was renamed. /api/v1/permission_templates is /api/v2/permission-templates, with a hyphen.

Your API keys keep working as they are. Authentication is unchanged, and the new read-only and operation-scope restrictions are opt-in, so an existing key stays unrestricted until an administrator narrows it.

The API documentation is generated from v2 only. /api/docs/v1/json is removed, and the Swagger UI no longer shows a version selector. The unversioned /api/docs/json serves the v2 spec, so existing bookmarks keep working.

The 4.2.x, 4.3.x and 4.4.x lines still ship v1. If you are not ready to migrate, stay on one of those.

See the API Overview for the full v1-to-v2 comparison.

interface.application_url is now required for OIDC and SAML

Poweradmin no longer derives a host from the request when building URLs that leave the application. Under the official Docker image (FrankenPHP/Caddy) and under Apache's default UseCanonicalName Off, the web server takes SERVER_NAME from the client's Host header, so the previous fallback let a forged header steer the OAuth redirect_uri and the SAML entityID/ACS to another host.

If OIDC or SAML is enabled and interface.application_url is empty, single sign-on stops working until you set it. Set it to the full public URL of the install before upgrading:

return [
    'interface' => [
        'application_url' => 'https://dns.example.com/poweradmin',
    ],
];

The value must match the redirect URI already registered with your identity provider. For deployments that were working before the upgrade this is the host users already reach Poweradmin on, so no provider-side change is needed.

What changes per feature when application_url is empty:

Feature Behaviour
OIDC login Fails with interface.application_url must be configured before OIDC can be used
SAML login and /saml/metadata Fail unless sp.entity_id, sp.assertion_consumer_service_url and sp.single_logout_service_url are all set explicitly, or the legacy interface.base_url is set (see below)
OIDC logout Still redirects to the provider, but without post_logout_redirect_uri, so users are not returned to the login page
Zone-access notification email Still sent, with the "Manage Zone" link omitted
API docs page Still works; the embedded spec URL becomes relative

This also ships in the 4.2.x, 4.3.x and 4.4.x patch releases. See OIDC and SAML.

A note on interface.base_url. SAML has always consulted this undocumented setting before falling back to the request. It still works, and it is still checked, but only after application_url. If you set it manually at some point, your SAML setup keeps working after the upgrade without any change. Prefer application_url going forward: it is the documented setting and the one OIDC, password-reset emails and the API docs page read. This is not the same setting as interface.base_url_prefix, which is unrelated and still current - that one handles subfolder deployments.

Credentials are hashed at rest

Password reset tokens. Reset tokens are now stored as sha256 hashes. Plaintext tokens issued before the upgrade cannot be validated afterwards - affected users simply request a new reset link; old rows expire within the token lifetime (1 hour by default).

API keys. Keys are stored as sha256$<hex> instead of plaintext. No action is needed: an existing key is migrated to its hash the first time it successfully authenticates, so integrations keep working with the key they already hold. Keys that are never used stay plaintext in the table until they are used or rotated.

Two things worth knowing:

  • A database backup taken before the upgrade contains plaintext keys. Restoring one after the upgrade has been running for a while gives you a table with some rows hashed and some plaintext, which still works but undoes the migration for anything restored.
  • Poweradmin can no longer show you an existing key's secret, because it no longer has it. This was already true of the interface before 4.5.0 - the secret is only ever displayed once, on the page shown right after creating or regenerating a key - but it is now true of the database as well. If you have lost a key, regenerate it.

Session keys written by the web installer should be rotated

The installer used to build security.session_key with mt_rand(), which is seeded from a 32-bit value, so the key carried far less entropy than its 46 characters suggest. 4.5.0 generates it with random_int() instead, but that only applies to keys created from now on. An existing key in config/settings.php is left exactly as it is, so upgrading on its own does not fix this.

If your key came from the web installer, replace it:

openssl rand -hex 32

Everyone is logged out when you change it, since sessions encrypted under the old key can no longer be read. Nothing else is affected. Installations set up through Docker already used openssl rand and need no action.

The header warning that flags a default key now also fires when the key is missing or shorter than 32 characters. It cannot tell a weak generator from a strong one, so a full-length installer key will not be flagged - rotate it if you are unsure where yours came from. See Security Policies.

Metadata and ownership view permissions are split out of content view

zone_content_view_own/zone_content_view_others no longer imply seeing zone metadata or zone owners. The migration auto-grants the new zone_metadata_view_* and zone_ownership_view_* permissions to every template holding the matching content-view permission, so nothing changes visibly on upgrade - but new templates need the permissions granted explicitly, and admins can now revoke them to hide those sections.

DNSSEC key management and zone logs need the new permissions

Before 4.5.0 a user who could edit their own zones (zone_content_edit_own) could also manage DNSSEC keys on them, zone_content_edit_others covered every zone, and anyone who could view zones could open their activity logs. From 4.5.0 DNSSEC key management needs zone_dnssec_manage_own (plus ownership) and the zone logs need zone_logs_view_own or zone_logs_view_others. Fresh installs grant these to the default Zone Manager, Editor and Viewer templates; the upgrade does not, so after it only administrators can manage DNSSEC keys or read the zone logs. There is no _others DNSSEC permission: editors of other users' zones keep DNSSEC access only to zones they own.

To keep what your users could do before, run this once after the update script. It works on MySQL/MariaDB, PostgreSQL and SQLite, and running it again changes nothing:

-- DNSSEC key management for templates that can edit zones
INSERT INTO perm_templ_items (templ_id, perm_id)
SELECT DISTINCT pti.templ_id, np.id
FROM perm_templ_items pti
JOIN perm_items cp ON cp.id = pti.perm_id
JOIN perm_items np ON np.name = 'zone_dnssec_manage_own'
WHERE cp.name IN ('zone_content_edit_own', 'zone_content_edit_others')
  AND NOT EXISTS (SELECT 1 FROM perm_templ_items x WHERE x.templ_id = pti.templ_id AND x.perm_id = np.id);

-- Zone logs for templates that can view zones
INSERT INTO perm_templ_items (templ_id, perm_id)
SELECT DISTINCT pti.templ_id, np.id
FROM perm_templ_items pti
JOIN perm_items cp ON cp.id = pti.perm_id
JOIN perm_items np ON np.name = CASE cp.name WHEN 'zone_content_view_own' THEN 'zone_logs_view_own' ELSE 'zone_logs_view_others' END
WHERE cp.name IN ('zone_content_view_own', 'zone_content_view_others')
  AND NOT EXISTS (SELECT 1 FROM perm_templ_items x WHERE x.templ_id = pti.templ_id AND x.perm_id = np.id);

The zone logs page also needs logging.database_enabled, as before.

LDAP identity fields become read-only when sync is enabled

With ldap.sync_user_info enabled, fullname and email of LDAP accounts are managed by the directory and read-only in the user editor. With sync disabled (the default), they remain editable as before.

Lua-enabling zone metadata is restricted to administrators

ENABLE-LUA-RECORDS and LUA-AXFR-SCRIPT make PowerDNS evaluate Lua, which reaches every zone the server hosts, so they can now only be set by a user holding user_is_ueberuser. Zone-level editors keep full access to every other metadata kind. Values an administrator has already set are left alone: the editor compares what was submitted against what is stored and only rejects an actual change, so a zone editor is not locked out of saving unrelated metadata on a zone that has these kinds set.

LUA records are restricted to editors above client level

LUA joins SOA and NS as a record type that zone_content_edit_own_as_client may not create, edit or delete. PowerDNS executes the script content of a LUA record, so it is not something a client-level tenant should be able to introduce. Owners and administrators (zone_content_edit_own / zone_content_edit_others) are unaffected, and existing LUA records keep resolving - only editing them through Poweradmin is gated.

Note this only ever mattered where the server has LUA enabled, either globally with enable-lua-records=yes or per zone via ENABLE-LUA-RECORDS - which the same release restricts to administrators.

The same restriction ships in 4.4.1 and 4.3.5, so it is not exclusive to this release. It also covers zone templates: a template record is written straight to the backend when the template is applied, so it never passes the record-level checks. Storing a LUA record in a template therefore requires zone_content_edit_own or zone_content_edit_others, a stricter bar than the client rule alone. Saving an existing zone as a template skips record types the caller may not author and names them in the resulting message rather than failing the whole save.

Identity providers can no longer grant administrator rights

oidc/saml/ldap.allow_superuser_provisioning defaults to false. With it unset, Poweradmin refuses to provision superuser from an IdP assertion in either of the two ways that previously worked:

  • a permission_template_mapping entry resolving to a template that grants user_is_ueberuser, and
  • a group_mapping entry resolving to a group whose template grants it - note the installer ships an Administrators group bound to exactly such a template.

If your deployment relies on the IdP deciding who is an administrator, those logins will stop being administrators after this upgrade. Both refusals are logged, so a login that silently loses rights can be traced. Either set the flag to true, or grant administrator rights inside Poweradmin, where an existing administrator has to act.

user_edit_templ_perm no longer hands out administrator rights

The permission delegates template management, not the granting of administrator rights. A holder who is not themselves an administrator can no longer assign a template that grants user_is_ueberuser - on the user editor, the user create form, or the REST API user endpoints - and can only change their own template if they also hold user_edit_others. Assigning any ordinary template is unaffected, so a delegated user administrator keeps working as before unless they were relying on handing out the Administrator template.

This ships across the supported lines, not only here: 4.4.1, 4.3.5, 4.2.6 and 3.9.12 carry the same rule.

Zone ids in API backend mode

This applies only to installs running the API backend that were migrated from SQL mode.

  • The API v2 zone list, GET /zones/{id} and the internal zone list now report the canonical zone id as id, the same value every other zone endpoint takes. Before, the list reported the zones table row id, which could open a different zone. Clients that stored the old ids (for example Terraform state or scripts) see them change once, and requests with an old id get 403 or 404.
  • On the first request after the upgrade, stored API key zone restrictions are moved from row ids to canonical ids. This runs once. A restriction that cannot be moved is kept but matches no zone, and the log names the key. Open each restricted key afterwards: the edit form shows a warning on a key that lost its zone, and the zone has to be selected again. Saving the key without a zone keeps it restricted; it does not become unrestricted. Until the move has run, a restricted key reaches no zone and new restrictions cannot be saved.
  • A new zone no longer takes a zone id that a migrated zone, a group grant or an API key restriction already uses. Before, it could take over a migrated zone's id and hide that zone.
  • A zone created in API mode before 4.5.0 may already share its id with a migrated zone. See Zone IDs Shared by Two Zones for how Poweradmin handles it.

Other changes

  • The Records column in zone lists is no longer sortable in API backend mode. Record counts are now resolved only for the zones on the current page, so ordering by them would sort just that page rather than the whole list. The column header is plain text instead of a sort link, and a stale count_records sort carried over in a session falls back to sorting by name. SQL backend mode is unaffected and still sorts by record count.
  • The MFA failure counter is tracked per authentication stage; repeated wrong MFA codes can lock an account even after a correct password.
  • Two authentication paths that were previously exempt now count towards account lockout: the dynamic DNS endpoint, and HTTP Basic Auth on the public API. This only matters if you have turned lockout on - security.account_lockout.enable_lockout ships disabled - but if you have, an automated client retrying with stale credentials can now lock the account it belongs to. Check any DDNS client or service account whose credentials may be out of date before upgrading. See Security Policies.
  • DELETE /api/v2/groups/{id} now returns 409 when the group still owns zones, unless the request carries confirm=true. Deleting such a group leaves those zones without an owner, so the confirmation is required. Groups that only have members are unaffected and delete as before. API clients that destroy zone-owning groups need the extra parameter.
  • interface.rows_per_page now accepts any value from 5 to 500 rather than silently falling back to 10 for anything outside 10/20/50/100. If you had set a value like 25 it was previously ignored, and will now take effect. A value outside 5 to 500 is clamped to the nearest limit and a warning is logged; it does not stop the application from starting.
  • PHP's session garbage collection is raised to match interface.session_timeout. Sessions previously expired after PHP's 1440-second default rather than the configured 1800, which returned users to the login page without an explanation.
  • An unrecognised logging.level now falls back to info rather than silently suppressing everything below emergency. If you have a typo such as warn instead of warning and logging.type is native, log volume will increase noticeably until the value is corrected. The logger records one warning naming the rejected value.
  • All other new features are additive or opt-in: new tables start empty, new settings default to prior behavior, and new permissions (except the auto-granted view split) are not assigned to any template.

Rollback

To roll back to v4.4.0:

  1. Restore your database backup.
  2. Restore your file backup.
  3. Restart the web server.

The new tables and columns are harmless in earlier versions - they are simply unused. Note that password reset tokens issued by 4.5.0 (hashed) cannot be validated by 4.4.0; users would need to request a new link after a rollback.