Security Notes
The portals are a thin browser client. Most security controls (authentication, authorization, session management, data validation) are enforced by the UI Backend; the portals must not be the only line of defence.
Transport
-
Nginx always declares
listen 443 ssland reads/etc/nginx/ssl/friendly.crt
friendly.keyfrom the mounted certificate directory. TLS is restricted to 1.2/1.3 with ECDHE-GCM ciphers. See Installation & Deployment — TLS Certificate. -
Use a CA-issued certificate in production. The self-signed pair in
docker/ssl/and thedocker/generate-cert.shoutput are development material only. -
Redirect or firewall plain HTTP if required by your environment.
Backend trust boundary
-
FT_UI_BACKEND_KEYWORDauthenticates the startup permissions upload to the backend. Replace the development default in every non-development environment and keep it in sync with the backend. It is passed as theX-Internal-Keywordheader and must never reach a browser. -
FT_UI_BACKEND_URLis a container-side setting. Since the browser reaches the backend only through the portals origin, the backend port does not have to be published to end users — keep it firewalled where possible. -
METRICS_ALLOW_IPdefaults to0.0.0.0/0, which exposes/nginx_statusto anyone who can reach the portals port. Restrict it to the monitoring network. -
The
/grafana-ro/proxy setsproxy_ssl_verify off, so the upstream certificate is not validated. Keep that hop on a trusted network.
Session and token handling
-
The backend issues a JWT; the portals keep it in
sessionStorageandTokenInterceptorattaches it asAuthorization: Bearer <token>.sessionStorageis cleared when the tab closes, but it is still readable by any script running on the origin — keep third-party scripts out of the portals and treat XSS as a token-theft vector. -
AuthenticationGuardredirects unauthenticated navigation to/loginand enforces the change-password flow;PermissionsGuard,TabsPermissionsGuardandMenuItemsPermissionsGuardhide what the user group is not allowed to use. These are UX guards — the backend re-checks every permission. -
UserInactivityServicedrives the idle timeout in the UI.
Client-side considerations
-
Rely on Angular’s built-in sanitization.
DomSanitizer.bypassSecurityTrustResourceUrlis used for the embedded Grafana and Superset frames; keep those URLs coming from backend settings, never from user input. -
Keep dependencies patched and run a dependency vulnerability scan in CI.
-
Portal permissions are uploaded from the image at every start, so an image and a backend that disagree about permissions resolve in favour of the image — review the shipped
app-permissions-config.jsonbefore deploying a new tag.