Skip to content

HTTP security headers

The Yontrack UI sends a set of security headers on every response — the desktop UI, the mobile UI under /mobile, its /api/* routes and its static files alike. The API keeps the default headers of Spring Security (X-Content-Type-Options, X-Frame-Options: DENY, Cache-Control: no-store).

Header Value
X-Content-Type-Options nosniff
Referrer-Policy strict-origin-when-cross-origin
Permissions-Policy denies the camera, the microphone, geolocation, payment, USB and the other browser features the UI does not use
Content-Security-Policy frame-ancestors 'self' — see Embedding Yontrack pages
X-Frame-Options SAMEORIGIN
Content-Security-Policy-Report-Only a full content security policy, reported but not enforced — see below

Content security policy

The complete policy is sent as report-only: a browser reports what the policy would block in its developer console, and blocks nothing. It allows inline styles, which the UI's component library injects at run time, and images from any HTTPS origin, which is where issue trackers serve their icons.

If you see [Report Only] messages in the browser console while using Yontrack, they are worth reporting as an issue: they are what decides whether the policy can be enforced in a later version.

Embedding Yontrack pages

By default, only Yontrack itself may display its pages in a frame. To embed Yontrack pages in another site — an internal portal, a wiki — set the YONTRACK_UI_FRAME_ANCESTORS environment variable on the UI container to the list of origins allowed to frame them, in the frame-ancestors source list syntax:

YONTRACK_UI_FRAME_ANCESTORS: "'self' https://portal.example.com"
  • the quotes around 'self' and 'none' are part of the value;
  • 'none' forbids framing altogether;
  • a value holding a ;, a , or a line break is refused and the default is kept — a warning is logged once.

The variable is read when a page is served, so changing it needs a restart of the UI container but no new image. It applies to the pages; the UI's /api/* routes and static files keep 'self', since framing them displays nothing.

X-Frame-Options: SAMEORIGIN is still sent: every current browser ignores it when frame-ancestors is present, and the few that do not know frame-ancestors keep refusing cross-origin framing.

Note

An embedded page still needs the user to be signed in, and the identity provider's own pages usually refuse to be framed. Yontrack's session cookie is SameSite=Lax, so a browser sends it to a frame only when the embedding site is on the same site as Yontrack — for example portal.example.com embedding yontrack.example.com. A page framed by another domain is shown signed out.

HSTS belongs to the ingress

Yontrack does not send Strict-Transport-Security, on purpose. HSTS tells browsers that a host — and, with includeSubDomains, every host under it — must only ever be reached over HTTPS. Only the component which terminates TLS in front of Yontrack knows whether that is true, so that is where it belongs: set it on your ingress controller, load balancer or reverse proxy. For example, with the NGINX ingress controller:

nginx.ingress.kubernetes.io/configuration-snippet: |
  more_set_headers "Strict-Transport-Security: max-age=31536000";

The same goes for any header you want to add or tighten beyond the ones listed above.