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 ssl and reads /etc/nginx/ssl/friendly.crt
    friendly.key from 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 the docker/generate-cert.sh output are development material only.

  • Redirect or firewall plain HTTP if required by your environment.

Backend trust boundary

  • FT_UI_BACKEND_KEYWORD authenticates 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 the X-Internal-Keyword header and must never reach a browser.

  • FT_UI_BACKEND_URL is 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_IP defaults to 0.0.0.0/0, which exposes /nginx_status to anyone who can reach the portals port. Restrict it to the monitoring network.

  • The /grafana-ro/ proxy sets proxy_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 sessionStorage and TokenInterceptor attaches it as Authorization: Bearer <token>. sessionStorage is 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.

  • AuthenticationGuard redirects unauthenticated navigation to /login and enforces the change-password flow; PermissionsGuard, TabsPermissionsGuard and MenuItemsPermissionsGuard hide what the user group is not allowed to use. These are UX guards — the backend re-checks every permission.

  • UserInactivityService drives the idle timeout in the UI.

Client-side considerations

  • Rely on Angular’s built-in sanitization. DomSanitizer.bypassSecurityTrustResourceUrl is 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.json before deploying a new tag.