A proxy for Nexus Repository Manager that allows for optional authentication against external identity providers.
Read the design document for a more detailed explanation of why and how.
ATTENTION: This software does not manage or enforce authorization. It's therefore required that users, roles and permissions are to be configured through Nexus administrative UI before start using Nexus.
ATTENTION: If GCP IAM authentication is enabled, every user account must be created with their organization email address as the username. A password needs to be set but it will only be important if GCP IAM authentication is disabled. Also it is necessary to grant the "Organization Viewer" role at organization-level (i.e., in the "IAM & Admin" section of the organization in the GCP UI) to every user.
ATTENTION:: If GCP IAM authentication is enabled, it is necessary to enable the Nexus "Rut Auth" capability. Otherwise, authentication succeeds but Nexus can't initiate user sessions.
ATTENTION: The Nexus-specific credentials mentioned above are valid for one year and for as long as the user is a member of the GCP organization.
ATTENTION: If the ENFORCE_HTTPS
flag is set to true
it is assumed that
one has configured nexus-proxy
or any load-balancers in front of it to serve
HTTPS on host NEXUS_HTTP_HOST
and port 443
with a valid TLS certificate.
ATTENTION:: Setting the JWT_REQUIRES_MEMBERSHIP_VERIFICATION
environment variable to false
inherently makes nexus-proxy
less secure.
In this scenario, a user containing a valid JWT token will be able to make requests using CLI tools like Maven or Docker without having to go through the OAuth2 consent screen.
For example, if a user leaves the organization while keeping a valid JWT token, and this environment variable is set to false
, they will still be able to make requests to Nexus.
While deploying Nexus Repository Manager on GKE, we identified a couple issues:
- GCLB backend health-checks weren't working when reaching Nexus directly.
- Couldn't expose Docker private registry with the same set-up used to expose the other artifact repositories.
We also knew beforehand that we would need to authenticate Nexus against Google Cloud Identity & Access Management.
While the aforementioned issues were easily fixed with Nginx, the authentication part proved much more complicated. For all of those reasons, we decided to implement our own proxy software that would deliver everything we needed.
Also, authentication is disabled by default so it can be used in simpler scenarios.
When GCP IAM authentication is enabled, every user attempting to access Nexus is asked to authenticate against GCP with their GCP organization credentials. If authentication succeeds, an encrypted token will be generated by the proxy and sent to the client ,e.g. browser, so it knows how to authenticate itself. After being logged-in, and only when authentication is enabled, the user must request Nexus-specific credentials for using with tools like Maven, Gradle, sbt, Python (pip) and Docker.
For building the project:
- JDK 8.
For basic proxying:
- A domain name configured with an
A
and aCNAME
records pointing to the proxy.- For local testing one may create two entries on
/etc/hosts
pointing to127.0.0.1
.
- For local testing one may create two entries on
- A running and properly configured instance of Nexus.
- One may use the default
8081
port for the HTTP connector and5003
for the Docker registry, for example.
- One may use the default
For opt-in authentication against Google Cloud IAM:
- All of the above.
- A GCP organization.
- A GCP project with the Cloud Resources Manager API enabled.
- A set of credentials of type OAuth Client ID obtained from GCP > API Manager > Credentials.
- Proper configuration of the resulting client's "Redirect URL".
A Java keystore is needed in order for the proxy to sign user tokens (JWT). Here's how to generate the keystore:
$ keytool -genkey \
-keystore keystore.jceks \
-storetype jceks \
-keyalg RSA \
-keysize 2048 \
-alias RS256 \
-sigalg SHA256withRSA \
-dname "CN=,OU=,O=,L=,ST=,C=" \
-validity 3651
One will be prompted for two passwords. One must make sure the passwords match.
Also, one is free to change the value of the dname
, keystore
and validity
parameters.
The following command will build the project and generate a runnable jar:
$ ./gradlew build
The following command will run the proxy on port 8080
with no authentication
and pointing to a local Nexus instance:
$ ALLOWED_USER_AGENTS_ON_ROOT_REGEX="GoogleHC" \
BIND_PORT="8080" \
NEXUS_DOCKER_HOST="containers.example.com" \
NEXUS_HTTP_HOST="nexus.example.com" \
NEXUS_RUT_HEADER="X-Forwarded-User" \
TLS_ENABLED="false" \
UPSTREAM_DOCKER_PORT="5000" \
UPSTREAM_HTTP_PORT="8081" \
UPSTREAM_HOST="localhost" \
java -jar ./build/libs/nexus-proxy-2.3.0.jar
The following command will run the proxy on port 8080
with GCP IAM
authentication enabled and pointing to a local Nexus instance:
$ ALLOWED_USER_AGENTS_ON_ROOT_REGEX="GoogleHC" \
AUTH_CACHE_TTL="60000" \
BIND_PORT="8080" \
CLOUD_IAM_AUTH_ENABLED="true" \
CLIENT_ID="my-client-id" \
CLIENT_SECRET="my-client-secret" \
KEYSTORE_PATH="./.secrets/keystore.jceks" \
KEYSTORE_PASS="my-keystore-password" \
NEXUS_DOCKER_HOST="containers.example.com" \
NEXUS_HTTP_HOST="nexus.example.com" \
NEXUS_RUT_HEADER="X-Forwarded-User" \
ORGANIZATION_ID="123412341234" \
REDIRECT_URL="https://nexus.example.com/oauth/callback" \
SESSION_TTL="1440000" \
TLS_ENABLED="false" \
UPSTREAM_DOCKER_PORT="5000" \
UPSTREAM_HTTP_PORT="8081" \
UPSTREAM_HOST="localhost" \
java -jar ./build/libs/nexus-proxy-2.3.0.jar
Name | Description |
---|---|
ALLOWED_USER_AGENTS_ON_ROOT_REGEX |
A regex against which to match the User-Agent of requests to GET / so that they can be answered with 200 OK . |
AUTH_CACHE_TTL |
The amount of time (in milliseconds) during which to cache the fact that a given user is authorized to make requests. |
BIND_HOST |
The interface on which to listen for incoming requests. Defaults to 0.0.0.0 . |
BIND_PORT |
The port on which to listen for incoming requests. |
CLIENT_ID |
The application's client ID in GCP / API Manager / Credentials. |
CLIENT_SECRET |
The abovementioned application's client secret. |
CLOUD_IAM_AUTH_ENABLED |
Whether to enable authentication against Google Cloud IAM. |
ENFORCE_HTTPS |
Whether to enforce access by HTTPS only. If set to true Nexus will only be accessible via HTTPS. |
JAVA_TOOL_OPTIONS |
JVM options to provide, for example -XX:MaxDirectMemorySize=1024M . |
JWT_REQUIRES_MEMBERSHIP_VERIFICATION |
Whether users presenting valid JWT tokens must still be verified for membership within the organization. |
KEYSTORE_PATH |
The path to the keystore containing the key with which to sign JWTs. |
KEYSTORE_PASS |
The password of the abovementioned keystore. |
LOG_LEVEL |
The desired log level (i.e., trace , debug , info , warn or error ). Defaults to info . |
NEXUS_DOCKER_HOST |
The host used to access the Nexus Docker registry. |
NEXUS_HTTP_HOST |
The host used to access the Nexus UI and Maven repositories. |
NEXUS_RUT_HEADER |
The name of the header which will convey auth info to Nexus. |
ORGANIZATION_ID |
The ID of the organization against which to validate users' membership. |
REDIRECT_URL |
The URL where to redirect users after the OAuth2 consent screen. |
SESSION_TTL |
The TTL (in milliseconds) of a user's session. |
TLS_CERT_PK12_PATH |
The path to the PK12 file to use when enabling TLS. |
TLS_CERT_PK12_PASS |
The password of the PK12 file to use when enabling TLS. |
TLS_ENABLED |
Whether to enable TLS. |
UPSTREAM_DOCKER_PORT |
The port where the proxied Nexus Docker registry listens. |
UPSTREAM_HTTP_PORT |
The port where the proxied Nexus instance listens. |
UPSTREAM_HOST |
The host where the proxied Nexus instance listens. |