Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RUBY-3303 Add OIDC machine workflow auth (WIP) #2873

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading