WebLogic Identity Asserter

The Maverics Identity Asserter is a custom WebLogic identity asserter module. This module validates that a request comes from a legitimate third-party authentication proxy, reads in a users’ identity from the request header, and returns either a distinguished name (DN) or a short name which WebLogic will use to build its identity context.

Server requirements

  • Oracle WebLogic 12c
  • Java 8

Install

To install the identity asserter, copy it to the $WL_HOME/server/lib/mbeantypes directory ($WL_HOME is the home directory of the WebLogic server).

  1. Under the target domain, select “Security Realms”.
  2. Select the target realm, e.g. “myrealm”, and then select “Providers” -> “Authentication”.
  3. Create a new Authentication Provider of type “MavericsIdentityAsserter”.
  4. Once created, re-order the security modules by moving the newly created asserter to the beginning of the list.
  5. Configure the flag as SUFFICIENT for Strata Identity Asserter.
  6. Generate the base64 encoded signature verification key as outlined in the example below.
  7. Select the “Provider Specific” tab and add the base64 encoded key you created. Signature verification key
  8. Configure the flag for all other security modules as SUFFICIENT.
  9. Restart the WebLogic server.
  10. Test the functionality by accessing the URL.

Upgrade

To upgrade the identity asserter, replace the existing Maverics Identity Asserter in the $WL_HOME/server/lib/mbeantypes directory with a new version. After replacing the JAR, restart the WebLogic server.

Example

This example demonstrates how the Orchestrator can be used to authenticate a user and proxy traffic to WebLogic. Once a request is received by WebLogic, the identity asserter will validate the MAVERICS_USERNAME header value and build the identity context.

In order to generate a private key that will be used to sign JWTs, the following command can be run. This example stores the generated private key in a secret store with a name of webLogicPrivateKey.

openssl genrsa -out wl-private-key.pem 2048

After a private key has been generated, the public key used for verifying the signature can be extracted with the following command.

openssl rsa -in wl-private-key.pem -outform PEM -pubout -out wl-public-key.pem

After the public key has been extracted, base64 encode it before adding it as a property in WebLogic. Base64 encoding the key is a requirement since WebLogic does not permit multi-line properties.

openssl base64 -A -in wl-public-key.pem

Now that the key pair has been generated and the necessary properties have been added to WebLogic, the below Maverics config file can be used as a reference for how to pass a JWT header value to WebLogic.

apps:
  - name: exampleWebLogic
    type: proxy
    routePatterns:
      - /
    upstream: https://app-internal.example.com
    headers:
      - createHeaderSE:
          funcName: CreateHeader
          file: /etc/maverics/webLogic-createHeader.go
          metadata:
            # userLookupKey is the key that will be used to look up the user's
            # username from the session store. The corresponding value will be passed
            # as the 'sub' claim in the JWT.
            userLookupKey: azure.name
            # privateKeyLookupKey is the key that will be used to retrieve the
            # signing key from the secret store.
            privateKeyLookupKey: webLogicPrivateKey
            # jwtLifetime is the lifetime of the JWT in hours. The value should be
            # set to match the lifetime of a user's session.
            jwtLifetime: 24

    policies:
      - location: /
        authentication:
          idps:
            - azure
        authorization:
          allowAll: true

/etc/maverics/webLogic-createHeader.go

package main

import (
	"errors"
	"fmt"
	"net/http"
	"time"

	"github.com/strata-io/service-extension/orchestrator"
	"github.com/strata-io/service-extension/weblogic"
)

// CreateHeader creates an HTTP header that will be consumed and validated by
// the WebLogic Identity Asserter. The header has a name of 'MAVERICS_USERNAME' and
// a signed JWT as the value.
func CreateHeader(
	api orchestrator.Orchestrator,
	_ http.ResponseWriter,
	_ *http.Request,
) (http.Header, error) {
	logger := api.Logger()
	logger.Debug("se", "creating header for WebLogic")

	metadata := api.Metadata()
	userLookupKey, _ := metadata["userLookupKey"].(string)
	privateKeyLookupKey, _ := metadata["privateKeyLookupKey"].(string)
	jwtLifetime, _ := metadata["jwtLifetime"].(int)

	session, err := api.Session()
	if err != nil {
		return nil, fmt.Errorf("failed to get session: %w", err)
	}
	name, err := session.GetString(userLookupKey)
	if err != nil {
		return nil, fmt.Errorf("failed to retrieve username from session: %w", err)
	}

	secretProvider, err := api.SecretProvider()
	if err != nil {
		return nil, fmt.Errorf("failed to retrieve secret provider: %w", err)
	}
	privateKey := secretProvider.GetString(privateKeyLookupKey)
	if len(privateKey) == 0 {
		return nil, errors.New("private key not found in secret provider")
	}

	jwt, err := api.WebLogic().NewSignedJWT(weblogic.Config{
		RSAPrivateKeyPEM: privateKey,
		Subject:          name,
		Lifetime:         time.Duration(jwtLifetime) * time.Hour,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to construct JWT: %w", err)
	}

	header := make(http.Header)
	header["MAVERICS_USERNAME"] = []string{jwt}
	return header, nil
}