Skip to content

yaegashi/azure-easy-auth-njs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Azure Easy Auth NJS Module

Introduction

This project provides an NJS (NGINX JavaScript) module for decoding and utilizing Azure Easy Auth headers in NGINX configurations.

It enables NGINX to interpret and leverage authentication information provided by Azure App Service or Azure Container Apps built-in authentication.

Module Capabilities

The azure_easy_auth.js module provides functionality to handle authentication data from Azure Easy Auth in NGINX environments. Key capabilities include:

  • HTTP Header Access: Provides direct access to Azure Easy Auth HTTP headers
    • getHeaderClientPrincipal(): Retrieves raw X-MS-CLIENT-PRINCIPAL header value
    • getHeaderClientPrincipalId(): Retrieves X-MS-CLIENT-PRINCIPAL-ID header directly
    • getHeaderClientPrincipalName(): Retrieves X-MS-CLIENT-PRINCIPAL-NAME header directly
    • getHeaderClientPrincipalIdp(): Retrieves X-MS-CLIENT-PRINCIPAL-IDP header directly
    • decodeHeaderClientPrincipal(): Decodes base64 header into JSON object
  • Identity Functions: Provides convenient methods to access common user identity attributes encoded in X-MS-CLIENT-PRINCIPAL:
    • getClaimEmail(): Retrieves user's email address
    • getClaimName(): Extracts display name
    • getClaimObjectId(): Obtains unique object identifier
    • getClaimPreferredUsername(): Gets preferred username
    • getClaimGroups(): Lists all group memberships
    • getClaimValue(), getClaimValues(): Accesses custom claim fields
  • Authorization Utilities: Simplifies access control implementation:
    • hasClaimGroup(): Verifies if user belongs to a specific group
    • isAuthorizedPrincipals(): Implements RBAC by checking user's object ID and group memberships against allowed principals

Azure Easy Auth exposes authentication context through HTTP headers as documented in Microsoft Learn:

  • X-MS-CLIENT-PRINCIPAL: Base64-encoded JSON containing all claims
  • X-MS-CLIENT-PRINCIPAL-ID: User's unique identifier
  • X-MS-CLIENT-PRINCIPAL-NAME: User's name or username
  • X-MS-CLIENT-PRINCIPAL-IDP: Identity provider identifier (e.g., "aad" for Microsoft Entra ID)

Docker Images

Overview

This project publishes customized Docker images based on the official NGINX images:

ghcr.io/yaegashi/azure-easy-auth-njs/nginx:VERSION

Here, VERSION corresponds to the base version, such as 1.27.4 or latest.

These images are designed to support the following projects:

Features

The Docker image includes the following features:

  • The content of the njs folder is available in /etc/nginx/njs.

  • The following directives are added to /etc/nginx/nginx.conf:

    load_module modules/ngx_http_js_module.so;
    js_path /etc/nginx/njs;
    js_path /nginx/njs;
    
  • You can mount a persistent volume, such as an Azure Files share, at /nginx in the container. When /nginx exists, the following persistent folders will be created:

    Persistent Folder Description
    /nginx/templates Configuration template folder. Symlinked to /etc/nginx/templates. The default default.conf.template will be placed if empty.
    /nginx/sites/default Default document root folder. The default index.html will be placed if empty.
    /nginx/njs NJS module folder. Included in the NJS search paths via js_path.
    /nginx/logs Log output folder.

    Additionally, it monitors /etc/nginx/templates and reloads the NGINX server when content changes are detected.

  • It launches an OpenSSH server (sshd) when the container runs on Azure App Service.

You can customize NGINX using /nginx/templates/default.conf.template in the persistent folder.

${NGINX_HOST} and ${NGINX_PORT} in template files will be replaced with the corresponding environment variables using envsubst.

Logging

Fluent Bit aggregates logs from NGINX (access/error) and other services (monitor) into a single container stdout stream. The output follows this format, with a timestamp, log origin, and the actual log message:

1743312054.855268 monitor 2025-03-30T05:20:54.85518 I: Change detected in /etc/nginx/templates
1743312054.860397 monitor 2025-03-30T05:20:54.86034 20-envsubst-on-templates.sh: Running envsubst on /etc/nginx/templates/default.conf.template to /etc/nginx/conf.d/default.conf
1743312054.869931 monitor 2025-03-30T05:20:54.86986 nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
1743312054.870034 monitor 2025-03-30T05:20:54.86997 nginx: configuration file /etc/nginx/nginx.conf test is successful
1743312054.870827 monitor 2025-03-30T05:20:54.87075 I: Reloading nginx service
1743312054.872446 monitor 2025-03-30T05:20:54.87156 ok: run: /docker/service/nginx: (pid 54) 3751s
1743312054.975864 error 2025/03/30 05:20:54 [notice] 2376#2376: gracefully shutting down
1743312054.975869 error 2025/03/30 05:20:54 [notice] 2375#2375: gracefully shutting down
1743312054.975870 error 2025/03/30 05:20:54 [notice] 2376#2376: exiting
1743312054.975870 error 2025/03/30 05:20:54 [notice] 2375#2375: exiting
1743312054.975871 error 2025/03/30 05:20:54 [notice] 2375#2375: exit
1743312054.975871 error 2025/03/30 05:20:54 [notice] 2376#2376: exit
1743312054.985042 error 2025/03/30 05:20:54 [notice] 54#54: signal 17 (SIGCHLD) received from 2375
1743312054.985065 error 2025/03/30 05:20:54 [notice] 54#54: worker process 2375 exited with code 0
1743312054.985148 error 2025/03/30 05:20:54 [notice] 54#54: worker process 2376 exited with code 0
1743312054.985169 error 2025/03/30 05:20:54 [notice] 54#54: signal 29 (SIGIO) received
1743312082.555455 access 172.18.0.1 - - [30/Mar/2025:05:21:22 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36" "10.240.2.23"
1743312088.008587 access 172.18.0.1 - - [30/Mar/2025:05:21:28 +0000] "GET /abc HTTP/1.1" 404 555 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36" "10.240.2.23"
1743312088.008511 error 2025/03/30 05:21:28 [error] 2408#2408: *6 open() "/nginx/sites/default/abc" failed (2: No such file or directory), client: 172.18.0.1, server: _, request: "GET /abc HTTP/1.1", host: "localhost:8080"

On container startup, it creates a log file in the persistent folder following this naming convention:

/nginx/logs/%Y/%m/%d/%Y%m%dT%H%M%S_{container_hostname}.log

The log file contains the same output that is streamed to stdout, providing both real-time monitoring and persistent logging capabilities.

Default Template Configuration

On the container starting, docker/default.conf.template are copied to /nginx/templates/default.conf.template if not exists.

server {
    listen ${NGINX_PORT} default_server;
    server_name _;

    root /nginx/sites/default;
}

Secure Path Configuration

Put the following in /nginx/template/default.conf.template:

server {
    listen ${NGINX_PORT};
    server_name _;

    root /nginx/sites/default;

    js_import auth from azure_easy_auth.js;
    js_set $is_authorized auth.isAuthorizedPrincipals;

    location ~ ^/secure/ {
        set $authorized_principals "98a7b6c5-d4e3-21f0-9g8h-765432109876";
        if ($is_authorized = "0") {
            return 403 "Forbidden";
        }
    }
}

This configuration secures paths beginning with /secure/ using the isAuthorizedPrincipals function. The function takes the $authorized_principals NGINX variable as input, which can contain multiple comma-separated GUIDs. The function returns "1" if any of these GUIDs match the authenticated user's object ID or group claims, or "0" if no match is found. Access is denied with a 403 response when the function returns "0".

Dynamic Secure Path Configuration

server {
    listen ${NGINX_PORT};
    server_name _;

    root /nginx/sites/default;

    js_import auth from azure_easy_auth.js;
    js_set $is_authorized auth.isAuthorizedPrincipals;
    set $super_principal "123e4567-e89b-12d3-a456-426614174000";

    location ~ ^/secure/(?<secured_principal>[^/]+) {
        set $authorized_principals "$secured_principal,$super_principal";
        if ($is_authorized = "0") {
            return 403 "Forbidden";
        }
    }
}

This configuration demonstrates dynamic permission setting based on the request path. It extracts a $secured_principal (GUID) from paths like /secure/{GUID}/... and combines it with an always-authorized $super_principal to create the $authorized_principals variable. Access is granted only if the user's object ID or group claims match either the $secured_principal or the $super_principal.

Multiple Subdomain Configuration

map $host $site_name {
    default default;
    ~*^${NGINX_HOST}$ default;
    ~*^(.+)\.${NGINX_HOST}$ $1;
}

server {
    listen ${NGINX_PORT};
    server_name .${NGINX_HOST};

    root /nginx/sites/$site_name;

    js_import auth from azure_easy_auth.js;
    js_set $is_authorized auth.isAuthorizedPrincipals;

    if ($site_name ~* ^pr-review-) {
        # Set PR reviewer's group principal
        set $authorized_principals "85b93f9c-7d2e-4a80-b71c-425ae32f1cc1";
        if ($is_authorized = "0") {
            return 403 "Forbidden";
        }
    }
}

server {
    listen ${NGINX_PORT} default_server;
    server_name _;
    return 404 "Not Found";
}

This configuration serves different content based on subdomains. When NGINX_HOST=example.com, it behaves as follows:

  • Access to the main domain (example.com) serves content from /nginx/sites/default
  • Regular subdomains (e.g., blog.example.com) serve content from their corresponding subdirectories (/nginx/sites/blog)
  • Pull request review subdomains (e.g., pr-review-123.example.com) are protected and only accessible by authenticated users in the group 85b93f9c-7d2e-4a80-b71c-425ae32f1cc1
  • Access to any undefined domain (e.g., example.net) returns a 404 error

This pattern is particularly useful in team development environments where you might create dedicated preview environments for each pull request, accessible only to specific reviewers.

About

Azure Easy Auth NJS Module

Topics

Resources

Stars

Watchers

Forks

Packages