Search code examples
dockerauthenticationmicroservices

Authentication between docker containers in microservices architecture, bypassing JWT auth for internal calls


I have the following docker architecture, orchestrated with a docker compose:

  • Service1: Frontend application, react app
  • Service2: ExpressJS server
  • Service3: Django server

The three applications communicate with each other through HTTP API calls. There is no limit on who can call who. Every application is externally exposed

The user authenticates on the frontend application (Service1) and it resolves a JWT that can be later passed to every other subsequent call, every other service has then an authentication layer to validate the JWT calls.

My question is: How would we validate calls from Service2 to Service3 without any user activity (without JWT)?

This is required for crons or maintenance jobs.

My first solution was to create a docker network with a fixed subnet in our docker-compose, like this:

networks:
  our-network:
    ipam:
      driver: default
      config:
        - subnet: "172.100.100.0/24"

Then, in Service2 and Service3, I allowed API calls coming from this subnet to be unauthenticated. I worry this could be a security problem and wonder if there are any better solution.


Solution

  • A JWT token is typically signed with a private key belonging to the token issuer. It is a good practice that JWT-tokens as an issuer claim.

    A JWT-token can typically be validated in two ways:

    • With a call to an OAuth token introspection endpoint of the issuer - but this adds more latency
    • Local validation with cached public key from the issuer - difficult to add logout functionality

    If you use the cached solution, that does not add much latency, the JWT issuer must implement a OpenID Discovery endpoint including a reference to the JWKS endpoint where the issuer publishes its public key.

    If you use Kubernetes, you can use a sidecar with OpenPolicyAgent that does the token validation for the app. It is not so fun for developers to implement token-validation functionality in every app, so this is an interesting alternative. You can also easyli add authorization rules with this solution.

    How would we validate calls from Service2 to Service3 without any user activity (without JWT)?

    When Service2 received the request from the user, authenticated with a JWT - it does the request to Service3 on-behalf-of the user, so it should pass the JWT token to Service3 as well - so that the Service3 can operate and only use resources that belong to the user, e.g. fetch from a database. This can be done without user intervention (until the JWT has expired).

    This is required for crons or maintenance jobs

    For Jobs - that is executed on-behalf-of the user, it is typically required to store a JWT-token with longer expiration time e.g. in a database. Typically this can be revoked by the user, and typically expire after 3 months or so, and then the user may need to authenticate again (or more than 3 months - depending on your needs). It is important that such token has lower permissions, typically only the permissions that is needed for the job.