OpenID Connect with Azure Active Directory

This guide explains how to configure OpenID Connect (OIDC) with Azure Active Directory as the identity provider.


This documentation applies to NGINX Instance Manager 2.0.0 and later.


Overview

Complete the steps in this guide to secure NGINX Instance Manager with OpenID Connect (OIDC) using Azure Active Directory (AD) as the identity provider. As an administrator, when you integrate OpenID authentication with NGINX Instance Manager, you can use role-based access control (RBAC) to limit user access to NGINX instances.

There are three steps to configuring NGINX Instance Manager to use OIDC and Azure Active Directory:

  1. Add users, including their email addresses, to Azure Active Directory.
  2. Add users to NGINX Instance Manager, using the same Azure Active Directory email addresses configured in step 1.
  3. Configure NGINX Plus in NGINX Instance Manager to use Azure Active Directory as the designated identity provider.

Before You Begin

To complete the instructions in this guide, you’ll need to perform the following tasks:


Configure Azure Active Directory

To configure Azure Active Directory as the identity provider, take the following steps:

Note:
The steps to configure Azure Active Directory require a premium account. If you have a free or standard account, you’ll need to upgrade.

Register an Application

  1. If you don’t already use Azure, create an account.

  2. Log in to the Azure portal.

  3. Select Azure Active Directory from the list of Azure services.

  4. On the left navigation menu, in the Manage section, select App registrations.

  5. Select New registration.

  6. Complete the following:

    • In the Name box, type the name of the application. For example, “NGINX Instance Manager”.
    • In the list of account types, select Account in this organizational directory only.
    • In the Redirect URI section, select Web and then type the redirect URI. For example, https://<my-nginx-instance-manager>/_codexch.

    Azure: register an application.

  7. Select Register.

  8. On the confirmation page, make a note of the following information. You’ll need to provide this information later to complete the setup.

    • Application (client) ID
    • Directory (tenant) ID

Create Client Secret

  1. On the left navigation menu, in the Manage section, select Certificates & secrets.

  2. Select New client secret.

  3. In the Description box, type a description for the client secret.

  4. Select Add. The client secret is added to the list with a secret string value and ID.

  5. Copy the Value for the client secret.

    Important:
    Save the client secret value in a safe location for future reference. Once you leave the page, you won’t be able to retrieve this value again.

Add Owners

  1. On the left navigation menu, in the Manage section, select Owners.

  2. Select Add owners.

  3. Search for the user you want to add, then select Select. Repeat this step for each user you’re adding.

    Warning:
    Make sure to add at least one user with administrative privileges. A failure to do so may lock admin users out of NGINX Instance Manager. If that happens, you can revert to Basic Auth to restore access.

Recreate Active Directory Users in NGINX Instance Manager

  1. Log in to NGINX Instance Manager as admin using a Basic Auth account.

  2. Select the Settings gear icon.

  3. In the Settings menu, select Users.

  4. Select Create.

  5. Provide the following information, making sure the details exactly match the user account you created in Azure Active Directory:

    • Email address
    • First Name
    • Last Name
    • Description
    • Roles
  6. Select Save.

  7. Repeat steps 5–6 until you’ve recreated all the users that you added to Azure Active Directory.

NGINX Instance Manager: add users.


Configure NGINX Plus to Interact with the Identity Provider

  1. Run the following command for your distribution to install the NGINX JavaScript module (njs), which is required to handle the interaction between NGINX Plus and the identity provider.

    sudo yum install nginx-plus-module-njs
    

    sudo apt install nginx-plus-module-njs
    
  2. On the NGINX Instance Manager server, open the /etc/nginx/nginx.conf file for editing and add the following directive to the top-level (“main”) section to load the NGINX JavaScript module:

    load_module modules/ngx_http_js_module.so;
    
  3. Open the /etc/nms/nginx/oidc/openid_configuration.conf file for editing. Using the example openid_configuration.conf below as a reference, replace the following variables in the file with the values you saved when configuring Azure Active Directory.


    example openid_configuration.conf
    # NGINX Management Suite - OpenID Connect configuration
    # Created for v. 2.0
    # (c) NGINX, Inc. 2021
    #
    # Each map block allows multiple values so that multiple IdPs can be supported,
    # the $host variable is used as the default input parameter but can be changed.
    #
    # Adapted from: https://github.com/nginxinc/nginx-openid-connect/blob/main/openid_connect_configuration.conf
    #
    # NOTE: This requires NGINX Plus with the NJS module installed and enabled
    # NOTE: The entries below need to be updated with values relevant to your environment and IdP
    #
    #       SERVER_FQDN is the Fully Qualified Domain Name of this server, e.g., nms.example.com
    #
    #       For more information about configuration and troubleshooting OIDC,
    #       refer to the reference repository at https://github.com/nginxinc/nginx-openid-connect
    
    map $host $oidc_authz_endpoint {
        SERVER_FQDN OIDC_AUTH_ENDPOINT;
           
        #default "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/auth";
        default "https://login.microsoftonline.com/{tenant_key}/oauth2/v2.0/authorize";
    }
    
    map $host $oidc_token_endpoint {
        SERVER_FQDN OIDC_TOKEN_ENDPOINT;
           
        #default "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/token";
        default "https://login.microsoftonline.com/{tenant_key}/oauth2/v2.0/token";
    }
    
    map $host $oidc_jwt_keyfile {
        SERVER_FQDN OIDC_KEYS_ENDPOINT;
           
        #default "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/certs";
        default "https://login.microsoftonline.com/{tenant_key}/discovery/v2.0/keys";
    }
    
    map $host $oidc_client {
        SERVER_FQDN OIDC_CLIENT_ID;
           
        #default "my-client-id";
        default "{client_key}";
    }
    
    map $host $oidc_pkce_enable {
        default 0;
    }
    
    map $host $oidc_client_secret {
        SERVER_FQDN OIDC_CLIENT_SECRET;
    
        #default "my-not-very-secret-client-secret";
        default "{client_secret}";
    }
    
    map $host $oidc_scopes {
        default "openid+profile+email+offline_access";
    }
    
    map $host $oidc_logout_redirect {
        # Where to send browser after requesting /logout location. This can be
        # replaced with a custom logout page, or complete URL.
        default "/_logout"; # Built-in, simple logout page
    }
    map $host $oidc_hmac_key {
        SERVER_FQDN OIDC_HMAC_KEY;
    
        # This should be unique for every NGINX instance/cluster
        default "ChangeMe";
    }
    
    map $proto $oidc_cookie_flags {
        http  "Path=/; SameSite=lax;"; # For HTTP/plaintext testing
        https "Path=/; SameSite=lax; HttpOnly; Secure;"; # Production recommendation
    }
    
    map $http_x_forwarded_port $redirect_base {
        ""      $proto://$host:$server_port;
        default $proto://$host:$http_x_forwarded_port;
    }
    
    map $http_x_forwarded_proto $proto {
        ""      $scheme;
        default $http_x_forwarded_proto;
    }
    
    # ADVANCED CONFIGURATION BELOW THIS LINE
    # Additional advanced configuration (server context) in openid_connect.conf
    
    # JWK Set will be fetched from $oidc_jwks_uri and cached here - ensure writable by nginx user
    proxy_cache_path /var/cache/nginx/jwk levels=1 keys_zone=jwk:64k max_size=1m;
    
    keyval_zone zone=oidc_id_tokens:1M state=/etc/nms/nginx/state/oidc_id_tokens.json timeout=1h;
    keyval_zone zone=refresh_tokens:1M state=/etc/nms/nginx/state/refresh_tokens.json timeout=8h;
    # Change timeout values to at least the validity period of each token type
    #keyval_zone zone=oidc_id_tokens:1M state=conf.d/oidc_id_tokens.json timeout=1h;
    #keyval_zone zone=refresh_tokens:1M state=conf.d/refresh_tokens.json timeout=8h;
    keyval_zone zone=oidc_pkce:128K timeout=90s; # Temporary storage for PKCE code verifier.
    
    keyval $cookie_auth_token $session_jwt zone=oidc_id_tokens;   # Exchange cookie for JWT
    keyval $cookie_auth_token $refresh_token zone=refresh_tokens; # Exchange cookie for refresh token
    keyval $request_id $new_session zone=oidc_id_tokens; # For initial session creation
    keyval $request_id $new_refresh zone=refresh_tokens; # ''
    keyval $pkce_id $pkce_code_verifier zone=oidc_pkce;
    
    auth_jwt_claim_set $jwt_audience aud; # In case aud is an array
    js_import oidc from /etc/nms/nginx/oidc/openid_connect.js;
    
    # vim: syntax=nginx
    

  4. Open the /etc/nginx/conf.d/nms-http.conf file for editing. Using the example nms-http.conf below as a reference, uncomment each of the OIDC settings and comment out the settings for Basic Auth.


    example nms-http.conf
    # NGINX Management Suite - Instance Manager configuration
    # Created for v. 2.0
    # (c) NGINX, Inc. 2021
    
    # If you choose to modify this file, create a backup of your changes.
    # This configuration file at its default location might be replaced by the
    # package management during upgrades
    
    # NOTE: enabling OIDC authentication requires using NGINX Plus
    #       with NJS installed and the module loaded in the config
    
    # Main routing logic
    map $uri $mapped_upstream {
        "~^/api/platform/v1/analytics"          core-api-service;
        "~^/api/platform/v1/license"            core-api-service;
        "~^/api/platform/v1/roles"              core-api-service;
        "~^/api/platform/v1/userinfo"           core-api-service;
        "~^/api/platform/v1/users"              core-api-service;
    
        "~^/api/platform/v1/analysis"           dpm-api-service;
        "~^/api/platform/v1/certs"              dpm-api-service;
        "~^/api/platform/v1/configs"            dpm-api-service;
        "~^/api/platform/v1/instance-groups"    dpm-api-service;
        "~^/api/platform/v1/instances"          dpm-api-service;
        "~^/api/platform/v1/servers"            dpm-api-service;
        "~^/api/platform/v1/systems"            dpm-api-service;
    
        default                                 core-api-service;
    }
    
    # REST to core services
    upstream core-api-service {
        zone core-api-service 64k;
    
        # Unix socket by default, optional TCP needs configuration in /etc/nms/nms.conf
        server unix:/var/run/nms/core.sock;
        #server 127.0.0.1:8033;
    
        keepalive 32;
    }
    
    # REST to DPM services
    upstream dpm-api-service {
        zone dpm-api-service 64k;
    
        # Unix socket by default, optional TCP needs configuration in /etc/nms/nms.conf
        server unix:/var/run/nms/dpm.sock;
        #server 127.0.0.1:8034;
    
        keepalive 32;
    }
    
    # gRPC to ingestion services
    upstream ingestion-grpc-service {
        zone ingestion-grpc-service 64k;
    
        # Unix socket by default, optional TCP needs configuration in /etc/nms/nms.conf
        server unix:/var/run/nms/ingestion.sock;
        #server 127.0.0.1:8035;
    
        keepalive 32;
    }
    
    # gRPC to DPM services
    upstream dpm-grpc-service {
        zone dpm-grpc-service 64k;
    
        # Unix socket by default, optional TCP needs configuration in /etc/nms/nms.conf
        server unix:/var/run/nms/am.sock;
        #server 127.0.0.1:8036;
    
        keepalive 32;
    }
    
    # WebSocket config, comment out if this section is duplicate via other config files
    map $http_upgrade $connection_upgrade {
        default upgrade;
        '' close;
    }
    
    # shared rate limiting memory zone definitions
    limit_req_zone $binary_remote_addr zone=nms-ratelimit:10m rate=20r/s;
    limit_req_zone $binary_remote_addr zone=nms-strict-ratelimit:10m rate=1r/s;
    
    # Additional upstreams provided by other modules
    include /etc/nms/nginx/upstreams/*.conf;
    
    # OIDC -- client configuration uncomment include to enable
    include /etc/nms/nginx/oidc/openid_configuration.conf;
    
    # Main external HTTPS server, needs port 443
    server {
        listen 443 ssl http2;
        root /var/www/nms;
        server_name _;
    
        # ssl_protocols       TLSv1.1 TLSv1.2 TLSv1.3; # TODO support for TLSv1.3?
        ssl_protocols       TLSv1.1 TLSv1.2;
        ssl_ciphers         HIGH:!aNULL:!MD5;
        ssl_session_cache   shared:SSL:10m;
        ssl_session_timeout 10m;
    
        ssl_certificate         /etc/nms/certs/manager-server.pem;
        ssl_certificate_key     /etc/nms/certs/manager-server.key;
        ssl_client_certificate  /etc/nms/certs/ca.pem;
    
        # OIDC Authentication: authorization code flow and Relying Party processing
        # OIDC remove comment from following include statement to enable
        include /etc/nms/nginx/oidc/openid_connect.conf;
    
        # Enable ONE of the two options according to HTTP Basic or OIDC authentication:
        # HTTP Basic:
        #proxy_set_header Nginx-Controller-User $remote_user;
        # OIDC:
        proxy_set_header Nginx-Controller-User $jwt_claim_unique_name;
        # NOTE: the username is dependent upon claims provided by your IdP
    
        # Optional OIDC settings: additional debug log
        error_log /var/log/nginx/oidc.log debug; # Reduce severity level as required
    
        keepalive_timeout       75s;
        client_max_body_size    50M; # Increase in case of large configuration files
        error_page 401 /401.json;
        error_page 403 /403.json;
    
        include /etc/nms/nginx/errors.http_conf;
        include /etc/nms/nginx/errors-grpc.server_conf;
    
        # Additional locations provided by other modules
        include /etc/nms/nginx/locations/*.conf;
    
        add_header X-Frame-Options sameorigin;
        add_header X-Content-Type-Options nosniff;
        add_header Cache-Control "no-cache, no-store, must-revalidate";
        add_header Pragma "no-cache";
        proxy_hide_header X-Powered-By;
        proxy_set_header Host $host;
        proxy_ssl_protocols TLSv1.1 TLSv1.2;
        proxy_headers_hash_max_size 512;
        proxy_headers_hash_bucket_size 128;
    
        ## For use with basic auth
        #auth_basic_user_file /etc/nms/nginx/.htpasswd;
    
        # For use with basic auth (comment out if using OIDC)
        #proxy_set_header Nginx-Controller-User $remote_user;
        # OIDC auth (uncomment to enable)
        proxy_set_header Nginx-Controller-User $jwt_claim_email;
        # proxy_set_header Nginx-Controller-User admin;
    
        proxy_set_header Authorization "";
        server_tokens off;
    
        # Object and method headers for Role-Based Access Control
        proxy_set_header object $request_uri;
        proxy_set_header http-method $request_method;
    
        # Redirect from / to /ui/
        location = / {
            absolute_redirect off;
            return 302 "/ui/";
        }
    
        location /api {
            # HTTP Basic authentication (disable if using OIDC)
            #auth_basic "Nginx Management Suite";
            # OIDC authentication (uncomment to enable)
            auth_jwt "" token=$session_jwt;
            auth_jwt_key_request /_jwks_uri;
    
            # Disabled proxy_ssl_* directives due to plaintext proxy_pass.
            # Re-enable them in case of advanced, customized multi-server installation.
            # proxy_ssl_trusted_certificate /etc/nms/certs/ca.pem;
            # proxy_ssl_certificate         /etc/nms/certs/manager-client.pem;
            # proxy_ssl_certificate_key     /etc/nms/certs/manager-client.key;
            # proxy_ssl_verify        on;
            # proxy_ssl_name          platform;
            # proxy_ssl_server_name   on;
    
            # Request limiting. Adjust limit_req_zone directive according to the server load
            limit_req zone=nms-ratelimit burst=10 nodelay;
            limit_req_status 429;
    
            # Dynamic proxying to the correct service, based on the corresponding map above
            proxy_pass http://$mapped_upstream;
        }
    
        # NGINX Management Suite modules service endpoint, provides inventory for the UI
        location = /modules {
            proxy_pass http://core-api-service/api/platform/v1/modules;
        }
    
        # Installation script for nginx-agent packages
        # NGINX Agent must be installed from this location to support local repository and offline environments
        location = /agent/install {
            return 302 '/install/nginx-agent';
        }
    
        location = /install/nginx-agent {
            sub_filter_types *;
            sub_filter '"CTR_FQDN"' '"$host"';
            # sub_filter_once on;
        }
    
        # UI static assets
        location /ui {
            # HTTP Basic authentication (comment if using OIDC auth)
            #auth_basic "Nginx Management Suite";
            # OIDC authentication (uncomment to enable)
            error_page 401 = @do_oidc_flow;
            auth_jwt "" token=$session_jwt;
            auth_jwt_key_request /_jwks_uri;
    
            add_header Cache-Control "private; max-age=86400";
            try_files $uri $uri/ /ui/index.html;
        }
    
        # Disabled authentication for UI manifest.json, loader can not provide auth at this time
        # TODO: move the file to /manifest.json URL, then remove this section
        location /ui/manifest.json {
            auth_basic off;
        }
    
        # gRPC service for metric ingestion
        location /f5.nginx.agent.sdk.MetricsService {
            include /etc/nms/nginx/errors-grpc.loc_conf;
            grpc_socket_keepalive on;
            grpc_read_timeout 5m;
            grpc_send_timeout 5m;
            client_body_timeout 10m;
            grpc_pass grpc://ingestion-grpc-service;
        }
    
        # gRPC service for DPM
        location /f5.nginx.agent.sdk.Commander {
            include /etc/nms/nginx/errors-grpc.loc_conf;
            grpc_socket_keepalive on;
            grpc_read_timeout 5m;
            grpc_send_timeout 5m;
            client_body_timeout 10m;
            grpc_pass grpc://dpm-grpc-service;
        }
    }
    

  5. Run sudo nginx -t to verify the config has no errors.

  6. Run sudo nginx -s reload to reload and apply the config.


Try It Out

Open NGINX Instance Manager by going to https://<your-nginx-instance-manager>/ui.

You should be redirected to Azure Active Directory. Log in with your Azure Active Directory email and password.