Loki’s authentication mechanisms are not directly enforced by Loki itself but rather by the gateway or proxy sitting in front of it.
Let’s see how this plays out with a common setup: Nginx acting as a reverse proxy for Loki, handling authentication before requests even reach Loki’s core.
Imagine a user trying to query Loki. Their request hits Nginx first.
server {
listen 8080;
server_name loki.example.com;
location / {
proxy_pass http://loki.local:3100; # Loki is running on port 3100 internally
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Authentication block - Example with Basic Auth
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
# If using JWT or OAuth, this is where you'd integrate those checks
# For JWT, you might use `jwt_auth` directive from ngx_http_jwt_module
# For OAuth, you'd typically redirect to an auth server and then validate tokens
}
}
In this Nginx configuration, auth_basic is enabled. This means Nginx will challenge the user with a popup asking for a username and password. The credentials are then checked against the /etc/nginx/.htpasswd file, which would contain entries like:
htpasswd -c /etc/nginx/.htpasswd myuser
New password:
Re-type new password:
# myuser:$apr1$abcdefgh$ijklmnop.qrstuvwxyz012345
If the credentials are valid, Nginx forwards the request to Loki. If not, Nginx returns a 401 Unauthorized or 403 Forbidden error. Loki itself never sees the invalid credentials.
This pattern is crucial because it allows you to offload authentication to a dedicated component. You can use standard Nginx modules for Basic Auth, or integrate with more sophisticated OAuth2/OIDC providers using third-party Nginx modules like lua-resty-openidc or commercial solutions. The key is that Loki receives requests that have already been authenticated by the proxy.
The "authentication" Nginx performs here is simple username/password validation. It doesn’t inspect the content of the logs or enforce fine-grained access control based on log labels. That level of control, if needed, would typically be implemented after authentication, either by a more advanced gateway or by a custom Loki adapter if you were building something highly specialized.
If you’re using Loki with Grafana, Grafana’s authentication is usually what you’re interacting with for UI access, and Grafana then makes authenticated requests to Loki on your behalf, often using its own internal user management and permissions. Grafana can be configured to use its own auth or delegate to external providers like OAuth or LDAP.
The request flow looks like this: User -> Grafana UI -> Grafana Backend (auth check) -> Grafana Backend (API call to Loki) -> Nginx Proxy (auth check for Loki) -> Loki.
The most surprising true thing about Loki’s authentication is that Loki has no built-in concept of users, passwords, or tokens; it trusts whatever the upstream proxy tells it.
Here’s how a request might be processed when Grafana is acting as the client to your authenticated Loki endpoint:
- User logs into Grafana: Grafana authenticates the user against its own user database or an external identity provider (e.g., OAuth, LDAP).
- User views logs in Grafana: Grafana constructs a query to Loki. Crucially, Grafana will typically add an
Authorizationheader to its request to the Nginx proxy. This header might contain a token for Basic Auth, a JWT, or a bearer token, depending on how you’ve configured Nginx and Grafana. - Nginx intercepts the request: Nginx receives the request from Grafana. It inspects the
Authorizationheader (or any other mechanism you’ve configured for authentication). - Nginx validates credentials: Nginx checks the provided credentials against its configured authentication method (e.g.,
.htpasswdfile, JWT validation module). - Nginx forwards to Loki: If Nginx successfully authenticates the request from Grafana, it forwards the request (often stripping the
Authorizationheader so Loki doesn’t see it) to the Loki backend. - Loki processes the request: Loki receives the request, assuming it’s valid because the proxy allowed it through, and returns the log data.
This setup means you can use any authentication mechanism Nginx supports, from simple Basic Auth to complex JWT validation or integration with OAuth2/OIDC providers. The critical point is that Loki itself remains unaware of the authentication process; it simply receives requests from its trusted proxy.
When you configure Grafana to talk to Loki, you’ll often set up Grafana’s data source for Loki with the proxy’s URL and the necessary credentials. For example, in Grafana’s data source configuration for Loki:
- URL:
http://loki.example.com:8080(your Nginx proxy address) - Authentication:
- If using Basic Auth: Username and Password fields.
- If using other methods, you might leave these blank and rely on Nginx’s header injection or other proxy-level mechanisms.
The one thing most people don’t immediately grasp is how Nginx’s proxy_set_header directives interact with authentication. While Nginx might validate an Authorization header, it’s common practice to remove or overwrite that header before proxying to Loki. This is done via proxy_set_header Authorization ""; or by not including the Authorization header in the proxy_set_header list. This ensures Loki doesn’t receive potentially sensitive authentication details, reinforcing the principle that Loki trusts its upstream proxy implicitly.
The next logical step is to consider authorization: how to grant specific users or groups access to specific log streams based on their labels, which is a capability Loki does have, but requires a more advanced proxy or custom implementation.