Skip to content

Commit

Permalink
RUBY-3303 Add OIDC machine workflow auth
Browse files Browse the repository at this point in the history
  • Loading branch information
durran committed Jul 23, 2024
1 parent c7b5736 commit 282e326
Show file tree
Hide file tree
Showing 43 changed files with 2,333 additions and 379 deletions.
161 changes: 160 additions & 1 deletion .evergreen/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,48 @@ functions:
CRYPT_SHARED_LIB_PATH="${CRYPT_SHARED_LIB_PATH}" SERVERLESS=1 SSL=ssl RVM_RUBY="${RVM_RUBY}" SINGLE_MONGOS="${SINGLE_MONGOS}" SERVERLESS_URI="${SERVERLESS_URI}" FLE="${FLE}" SERVERLESS_MONGODB_VERSION="${SERVERLESS_MONGODB_VERSION}" .evergreen/run-tests-serverless.sh
"run oidc vm tests":
- command: subprocess.exec
type: test
params:
working_dir: src
binary: bash
env:
DRIVERS_TOOLS: ${DRIVERS_TOOLS}
PROJECT_DIRECTORY: ${PROJECT_DIRECTORY}
RVM_RUBY: ${RVM_RUBY}
TEST_SCRIPT: ${TEST_SCRIPT}
args:
- .evergreen/${RUN_SCRIPT}

"run oidc prose tests":
- command: subprocess.exec
type: test
params:
working_dir: src
binary: bash
env:
DRIVERS_TOOLS: ${DRIVERS_TOOLS}
PROJECT_DIRECTORY: ${PROJECT_DIRECTORY}
ENVIRONMENT: ${ENVIRONMENT}
RVM_RUBY: ${RVM_RUBY}
args:
- .evergreen/run-tests-oidc-prose.sh

"run oidc unified tests":
- command: subprocess.exec
type: test
params:
working_dir: src
binary: bash
env:
DRIVERS_TOOLS: ${DRIVERS_TOOLS}
PROJECT_DIRECTORY: ${PROJECT_DIRECTORY}
ENVIRONMENT: ${ENVIRONMENT}
RVM_RUBY: ${RVM_RUBY}
args:
- .evergreen/run-tests-oidc-unified.sh

pre:
- func: "fetch source"
- func: "create expansions"
Expand Down Expand Up @@ -721,6 +763,77 @@ task_groups:
tasks:
- testazurekms-task

- name: test_oidc_task_group
setup_group:
- func: fetch source
- func: create expansions
- command: ec2.assume_role
params:
role_arn: ${aws_test_secrets_role}
- command: subprocess.exec
params:
binary: bash
include_expansions_in_env:
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
- AWS_SESSION_TOKEN
env:
MONGODB_VERSION: '8.0'
args:
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/setup.sh
setup_group_can_fail_task: true
setup_group_timeout_secs: 1800
tasks:
- oidc-auth-test-latest

- name: test_oidc_azure_task_group
setup_group:
- func: fetch source
- func: create expansions
- command: shell.exec
params:
shell: bash
script: |-
set -o errexit
${PREPARE_SHELL}
export AZUREOIDC_VMNAME_PREFIX="RUBY_DRIVER"
$DRIVERS_TOOLS/.evergreen/auth_oidc/azure/setup.sh
teardown_task:
- command: shell.exec
params:
shell: bash
script: |-
${PREPARE_SHELL}
$DRIVERS_TOOLS/.evergreen/auth_oidc/azure/teardown.sh
setup_group_can_fail_task: true
setup_group_timeout_secs: 1800
tasks:
- oidc-auth-test-azure-latest

- name: test_oidc_gcp_task_group
setup_group:
- func: fetch source
- func: create expansions
- command: shell.exec
params:
shell: bash
script: |-
set -o errexit
${PREPARE_SHELL}
export GCPOIDC_VMNAME_PREFIX="RUBY_DRIVER"
$DRIVERS_TOOLS/.evergreen/auth_oidc/gcp/setup.sh
teardown_task:
- command: shell.exec
params:
shell: bash
script: |-
${PREPARE_SHELL}
$DRIVERS_TOOLS/.evergreen/auth_oidc/gcp/teardown.sh
setup_group_can_fail_task: true
setup_group_timeout_secs: 1800
tasks:
- oidc-auth-test-gcp-latest

tasks:
- name: "test-atlas"
commands:
Expand Down Expand Up @@ -865,8 +978,41 @@ tasks:
LAMBDA_STACK_NAME: "dbx-ruby-lambda"
RVM_RUBY: ruby-3.2
MONGODB_URI: ${MONGODB_URI}
axes:

- name: oidc-auth-test-latest
commands:
- func: "run oidc vm tests"
vars:
TEST_SCRIPT: run-tests-oidc-prose.sh
RUN_SCRIPT: run-tests-oidc-test.sh
- func: "run oidc vm tests"
vars:
TEST_SCRIPT: run-tests-oidc-unified.sh
RUN_SCRIPT: run-tests-oidc-test.sh

- name: oidc-auth-test-azure-latest
commands:
- func: "run oidc vm tests"
vars:
TEST_SCRIPT: run-tests-oidc-prose.sh
RUN_SCRIPT: run-tests-oidc-azure.sh
- func: "run oidc vm tests"
vars:
TEST_SCRIPT: run-tests-oidc-unified.sh
RUN_SCRIPT: run-tests-oidc-azure.sh

- name: oidc-auth-test-gcp-latest
commands:
- func: "run oidc vm tests"
vars:
TEST_SCRIPT: run-tests-oidc-prose.sh
RUN_SCRIPT: run-tests-oidc-gcp.sh
- func: "run oidc vm tests"
vars:
TEST_SCRIPT: run-tests-oidc-unified.sh
RUN_SCRIPT: run-tests-oidc-gcp.sh

axes:
- id: preload
display_name: Preload server
values:
Expand Down Expand Up @@ -1856,3 +2002,16 @@ buildvariants:
display_name: "AWS Lambda"
tasks:
- name: test_aws_lambda_task_group

- matrix_name: test-oidc-variant
matrix_spec:
ruby: "ruby-3.2"
fle: helper
topology: standalone
os: ubuntu2204
mongodb-version: latest
display_name: "OIDC auth tests: latest ruby-3.2"
tasks:
- test_oidc_task_group
- test_oidc_azure_task_group
- test_oidc_gcp_task_group
9 changes: 9 additions & 0 deletions .evergreen/run-tests-oidc-azure.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash
set -o xtrace # Write all commands first to stderr
set -o errexit # Exit the script with error if any of the commands fail

export AZUREOIDC_DRIVERS_TAR_FILE=/tmp/mongo-ruby-driver.tgz
tar czf $AZUREOIDC_DRIVERS_TAR_FILE .
export AZUREOIDC_TEST_CMD="source ./env.sh && ENVIRONMENT=azure RVM_RUBY=${RVM_RUBY} ./.evergreen/${TEST_SCRIPT}"
export PROJECT_DIRECTORY=$PROJECT_DIRECTORY
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/azure/run-driver-test.sh
9 changes: 9 additions & 0 deletions .evergreen/run-tests-oidc-gcp.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash
set -o xtrace # Write all commands first to stderr
set -o errexit # Exit the script with error if any of the commands fail

export GCPOIDC_DRIVERS_TAR_FILE=/tmp/mongo-ruby-driver.tgz
tar czf $GCPOIDC_DRIVERS_TAR_FILE .
export GCPOIDC_TEST_CMD="source ./secrets-export.sh drivers/gcpoidc && ENVIRONMENT=gcp RVM_RUBY=${RVM_RUBY} ./.evergreen/${TEST_SCRIPT}"
export PROJECT_DIRECTORY=$PROJECT_DIRECTORY
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/gcp/run-driver-test.sh
24 changes: 24 additions & 0 deletions .evergreen/run-tests-oidc-prose.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash

set -ex

ENVIRONMENT=${ENVIRONMENT:-"test"}

. `dirname "$0"`/../spec/shared/shlib/distro.sh
. `dirname "$0"`/../spec/shared/shlib/set_env.sh
. `dirname "$0"`/functions.sh

set_env_vars
set_env_python
set_env_ruby

sudo apt-get -y install libyaml-dev cmake

bundle_install
bundle exec rspec -fd spec/integration/oidc/${ENVIRONMENT}_machine_auth_flow_prose_spec.rb

test_status=$?

kill_jruby

exit ${test_status}
9 changes: 9 additions & 0 deletions .evergreen/run-tests-oidc-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash
set -o xtrace # Write all commands first to stderr
set -o errexit # Exit the script with error if any of the commands fail

source $DRIVERS_TOOLS/.evergreen/auth_oidc/secrets-export.sh
export PROJECT_DIRECTORY=$PROJECT_DIRECTORY
export ENVIRONMENT=$ENVIRONMENT
export AWS_WEB_IDENTITY_TOKEN_FILE=$OIDC_TOKEN_FILE
bash ./.evergreen/${TEST_SCRIPT}
Empty file.
2 changes: 1 addition & 1 deletion .mod/drivers-evergreen-tools
Submodule drivers-evergreen-tools updated 57 files
+3 −0 .evergreen/atlas/setup.sh
+1 −1 .evergreen/atlas_data_lake/pull-mongohouse-image.sh
+1 −1 .evergreen/atlas_data_lake/run-mongohouse-image.sh
+1 −1 .evergreen/auth_aws/aws_setup.sh
+1 −14 .evergreen/auth_oidc/azure/create-and-setup-vm.sh
+1 −2 .evergreen/auth_oidc/azure/setup-secrets.sh
+1 −1 .evergreen/auth_oidc/docker_entry.sh
+1 −2 .evergreen/auth_oidc/gcp/setup-secrets.sh
+1 −1 .evergreen/auth_oidc/gcp/setup.sh
+9 −0 .evergreen/auth_oidc/k8s/README.md
+17 −0 .evergreen/auth_oidc/k8s/remote-scripts/run-self-test.sh
+23 −0 .evergreen/auth_oidc/k8s/remote-scripts/test.py
+48 −0 .evergreen/auth_oidc/k8s/run-driver-test.sh
+8 −0 .evergreen/auth_oidc/k8s/setup-secrets.sh
+88 −0 .evergreen/auth_oidc/k8s/setup.sh
+18 −0 .evergreen/auth_oidc/k8s/teardown.sh
+1 −2 .evergreen/auth_oidc/setup-secrets.sh
+1 −1 .evergreen/auth_oidc/start_local_server.sh
+13 −0 .evergreen/check-connection.sh
+75 −15 .evergreen/config.yml
+1 −14 .evergreen/csfle/azurekms/create-and-setup-vm.sh
+14 −0 .evergreen/csfle/azurekms/login.sh
+3 −5 .evergreen/csfle/azurekms/remote-scripts/setup-azure-vm.sh
+1 −1 .evergreen/csfle/gcpkms/download-gcloud.sh
+2 −4 .evergreen/csfle/gcpkms/remote-scripts/setup-gce-instance.sh
+6 −6 .evergreen/docker/ubuntu18.04/Dockerfile
+1 −1 .evergreen/docker/ubuntu18.04/base-entrypoint.sh
+2 −1 .evergreen/docker/ubuntu18.04/test-entrypoint.sh
+2 −2 .evergreen/docker/ubuntu20.04/Dockerfile
+2 −1 .evergreen/docker/ubuntu20.04/test-entrypoint.sh
+6 −1 .evergreen/download-mongodb.sh
+93 −0 .evergreen/ensure-binary.sh
+1 −3 .evergreen/github_app/apply-labels.sh
+1 −3 .evergreen/github_app/assign-reviewer.sh
+1 −3 .evergreen/github_app/create_or_modify_comment.sh
+5 −0 .evergreen/handle-paths.sh
+3 −2 .evergreen/install-dependencies.sh
+7 −0 .evergreen/k8s/aks/README.md
+52 −0 .evergreen/k8s/aks/setup-cluster.sh
+51 −0 .evergreen/k8s/aks/setup.sh
+19 −0 .evergreen/k8s/aks/teardown-cluster.sh
+15 −0 .evergreen/k8s/aks/teardown.sh
+27 −0 .evergreen/k8s/configure-pod.sh
+8 −0 .evergreen/k8s/gke/README.md
+29 −0 .evergreen/k8s/gke/setup-cluster.sh
+55 −0 .evergreen/k8s/gke/setup.sh
+18 −0 .evergreen/k8s/gke/teardown-cluster.sh
+13 −0 .evergreen/k8s/gke/teardown.sh
+11 −0 .evergreen/k8s/remote-scripts/setup-pod.sh
+1 −1 .evergreen/ocsp/mock-ocsp-responder-requirements.txt
+1 −1 .evergreen/secrets_handling/setup-secrets.sh
+1 −1 .evergreen/serverless/setup-secrets.sh
+4 −0 .evergreen/serverless/setup.sh
+11 −4 .evergreen/teardown.sh
+2 −1 .evergreen/venv-utils.sh
+2 −0 .gitignore
+1 −1 .pre-commit-config.yaml
4 changes: 3 additions & 1 deletion lib/mongo/auth.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
require 'mongo/auth/cr'
require 'mongo/auth/gssapi'
require 'mongo/auth/ldap'
require 'mongo/auth/oidc'
require 'mongo/auth/scram'
require 'mongo/auth/scram256'
require 'mongo/auth/x509'
Expand Down Expand Up @@ -70,6 +71,7 @@ module Auth
aws: Aws,
gssapi: Gssapi,
mongodb_cr: CR,
mongodb_oidc: Oidc,
mongodb_x509: X509,
plain: LDAP,
scram: Scram,
Expand All @@ -89,7 +91,7 @@ module Auth
# value of speculativeAuthenticate field of hello response of
# the handshake on the specified connection.
#
# @return [ Auth::Aws | Auth::CR | Auth::Gssapi | Auth::LDAP |
# @return [ Auth::Aws | Auth::CR | Auth::Gssapi | Auth::LDAP | Auth::Oidc
# Auth::Scram | Auth::Scram256 | Auth::X509 ] The authenticator.
#
# @since 2.0.0
Expand Down
94 changes: 94 additions & 0 deletions lib/mongo/auth/oidc.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# frozen_string_literal: true
# rubocop:todo all

# Copyright (C) 2024 MongoDB, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

module Mongo
module Auth

# Defines behavior for OIDC authentication.
#
# @api private
class Oidc < Base
attr_reader :speculative_auth_result, :cache, :machine_workflow

# The authentication mechanism string.
#
# @since 2.20.0
MECHANISM = 'MONGODB-OIDC'.freeze

# Initializes the OIDC authenticator.
#
# @param [ Auth::User ] user The user to authenticate.
# @param [ Mongo::Connection ] connection The connection to authenticate over.
#
# @option opts [ BSON::Document | nil ] speculative_auth_result The
# value of speculativeAuthenticate field of hello response of
# the handshake on the specified connection.
def initialize(user, connection, **opts)
super
@cache = TokenCache.new
@speculative_auth_result = opts[:speculative_auth_result]
@machine_workflow = MachineWorkflow::new(
auth_mech_properties: user.auth_mech_properties,
username: user.name
)
end

# Log the user in on the current connection.
#
# @return [ BSON::Document ] The document of the authentication response.
def login
execute_workflow(connection: connection, conversation: conversation)
end

private

def execute_workflow(connection:, conversation:)
# If there is a cached access token, try to authenticate with it. If
# authentication fails with an Authentication error (18),
# invalidate the access token, fetch a new access token, and try
# to authenticate again.
# If the server fails for any other reason, do not clear the cache.
if cache.access_token?
token = cache.access_token
msg = conversation.start(connection: connection, token: token)
begin
dispatch_msg(connection, conversation, msg)
rescue AuthError => error
cache.invalidate(token: token)
execute_workflow(connection: connection, conversation: conversation)
end
end
# This is the normal flow when no token is in the cache. Execute the
# machine callback to get the token, put it in the caches, and then
# send the saslStart to the server.
token = machine_workflow.execute
if token.nil? || !token[:access_token]
raise Error::OidcError,
"OIDC machine workflows must return a valid response with an access token but #{token} was returned"
end
cache.access_token = token[:access_token]
connection.access_token = token[:access_token]
msg = conversation.start(connection: connection, token: token[:access_token])
dispatch_msg(connection, conversation, msg)
end
end
end
end

require 'mongo/auth/oidc/conversation'
require 'mongo/auth/oidc/machine_workflow'
require 'mongo/auth/oidc/token_cache'
Loading

0 comments on commit 282e326

Please sign in to comment.