From eee14f50fc6ef58efeb66a762204d2945b91343c Mon Sep 17 00:00:00 2001 From: treydock Date: Wed, 15 Sep 2021 11:02:41 -0400 Subject: [PATCH] Support Dex local users (#1335) * Support Dex local users * Mount project root into dev container * Ensure removal of bcrypt from Gemfile is reflected in Gemfile.lock * Revert "Mount project root into dev container" This reverts commit 4d319bf5a6354e807c9522239c485244beac0108. * Remove mock connector and only allow Dex local user if no connectors * Only allow local users with Dex via --insecure flag * Add insecure flag to package testing, needed to get local dex user * Use --insecure flag for package tests too * Fix how rubocop is required --- Dockerfile | 2 +- Gemfile | 1 - Gemfile.lock | 2 - docker/dev-entrypoint.sh | 6 +-- docker/launch-ood | 2 +- lib/tasks/development.rb | 35 +++++----------- ood-portal-generator/Gemfile | 1 + ood-portal-generator/Gemfile.lock | 2 + .../lib/ood_portal_generator/application.rb | 10 ++++- .../lib/ood_portal_generator/dex.rb | 40 ++++++++++++++----- ood-portal-generator/spec/application_spec.rb | 25 ++++++++++++ .../spec/fixtures/dex.yaml.default | 7 +--- .../spec/fixtures/dex.yaml.static_passwords | 27 +++++++++++++ packaging/ondemand.spec | 1 - spec/e2e/e2e_helper.rb | 5 ++- 15 files changed, 113 insertions(+), 53 deletions(-) create mode 100644 ood-portal-generator/spec/fixtures/dex.yaml.static_passwords diff --git a/Dockerfile b/Dockerfile index 7724d9f83a..8ec619491f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -70,7 +70,7 @@ Defaults:apache env_keep += "NGINX_STAGE_* OOD_*" \n\ apache ALL=(ALL) NOPASSWD: /opt/ood/nginx_stage/sbin/nginx_stage' >/etc/sudoers.d/ood # run the OOD executables to setup the env -RUN /opt/ood/ood-portal-generator/sbin/update_ood_portal +RUN /opt/ood/ood-portal-generator/sbin/update_ood_portal --insecure RUN /opt/ood/nginx_stage/sbin/update_nginx_stage RUN echo $VERSION > /opt/ood/VERSION # this one bc centos:8 doesn't generate localhost cert diff --git a/Gemfile b/Gemfile index 15ca57e828..86af846f84 100644 --- a/Gemfile +++ b/Gemfile @@ -5,7 +5,6 @@ source "https://rubygems.org" git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } gem "rake" -gem "bcrypt" group :test do gem "rspec" diff --git a/Gemfile.lock b/Gemfile.lock index 1a5aecdce4..2dba537b49 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -12,7 +12,6 @@ GEM specs: ansi (1.5.0) ast (2.4.2) - bcrypt (3.1.16) beaker (4.30.0) beaker-hostgenerator hocon (~> 1.0) @@ -185,7 +184,6 @@ PLATFORMS ruby DEPENDENCIES - bcrypt beaker beaker-docker! beaker-rspec diff --git a/docker/dev-entrypoint.sh b/docker/dev-entrypoint.sh index d1cd1c0ce7..47967dcec0 100755 --- a/docker/dev-entrypoint.sh +++ b/docker/dev-entrypoint.sh @@ -13,11 +13,7 @@ sudo su root <> /etc/ood/dex/config.yaml - fi + /opt/ood/ood-portal-generator/sbin/update_ood_portal --force --insecure SETUP sudo runuser -u ondemand-dex /usr/sbin/ondemand-dex serve /etc/ood/dex/config.yaml & diff --git a/docker/launch-ood b/docker/launch-ood index 9266c429f1..afdef06269 100755 --- a/docker/launch-ood +++ b/docker/launch-ood @@ -2,6 +2,6 @@ set -e -/opt/ood/ood-portal-generator/sbin/update_ood_portal +/opt/ood/ood-portal-generator/sbin/update_ood_portal --force --insecure runuser -u ondemand-dex /usr/sbin/ondemand-dex serve /etc/ood/dex/config.yaml & /usr/sbin/httpd -DFOREGROUND diff --git a/lib/tasks/development.rb b/lib/tasks/development.rb index d0e0100cfc..7b86dc455d 100644 --- a/lib/tasks/development.rb +++ b/lib/tasks/development.rb @@ -3,7 +3,6 @@ namespace :dev do require_relative 'build_utils' require 'yaml' - require 'bcrypt' include BuildUtils def dev_container_name @@ -14,6 +13,10 @@ def init_ood_portal file = "#{config_directory}/ood_portal.yml" return if File.exist?(file) + require 'io/console' + puts 'Enter password:' + plain_password = $stdin.noecho(&:gets).chomp + File.open(file, File::WRONLY|File::CREAT|File::EXCL) do |f| f.write({ 'servername': 'localhost', @@ -25,35 +28,18 @@ def init_ood_portal 'type': 'mockCallback', 'id': 'mock', 'name': 'Mock' + }], + 'static_passwords': [{ + 'email': "#{user.name}@localhost", + 'password': plain_password, + 'username': "#{user.name}", + 'userID': "71e63e31-7af3-41d7-add2-575568f4525f" }] } }.to_yaml) end end - def init_ctr_user - file = "#{config_directory}/static_user.yml" - return if File.exist?(file) - - require 'io/console' - puts 'Enter password:' - plain_password = $stdin.noecho(&:gets).chomp - bcrypted = BCrypt::Password.create(plain_password) - - content = <<~CONTENT - enablePasswordDB: true - staticPasswords: - - email: "#{user.name}@localhost" - hash: "#{bcrypted}" - username: "#{user.name}" - userID: "71e63e31-7af3-41d7-add2-575568f4525f" - CONTENT - - File.open(file, File::WRONLY | File::CREAT | File::EXCL) do |f| - f.write(content) - end - end - def container_rt_args podman_runtime? ? podman_rt_args : docker_rt_args end @@ -134,7 +120,6 @@ def dev_mounts task :ensure_dev_files do [ :init_ood_portal, - :init_ctr_user ].each do |initer| send(initer) end diff --git a/ood-portal-generator/Gemfile b/ood-portal-generator/Gemfile index 11a1a60b19..00158076fc 100644 --- a/ood-portal-generator/Gemfile +++ b/ood-portal-generator/Gemfile @@ -6,6 +6,7 @@ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } gem 'activesupport', '5.2.4.3' gem 'dotenv', '~> 2.1' +gem "bcrypt" group :development, :test do gem "rake" diff --git a/ood-portal-generator/Gemfile.lock b/ood-portal-generator/Gemfile.lock index 7e090bec46..6d24267fc5 100644 --- a/ood-portal-generator/Gemfile.lock +++ b/ood-portal-generator/Gemfile.lock @@ -6,6 +6,7 @@ GEM i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) + bcrypt (3.1.16) climate_control (0.2.0) concurrent-ruby (1.1.6) diff-lcs (1.3) @@ -36,6 +37,7 @@ PLATFORMS DEPENDENCIES activesupport (= 5.2.4.3) + bcrypt climate_control dotenv (~> 2.1) rake diff --git a/ood-portal-generator/lib/ood_portal_generator/application.rb b/ood-portal-generator/lib/ood_portal_generator/application.rb index 7a86985691..4f81b867a1 100644 --- a/ood-portal-generator/lib/ood_portal_generator/application.rb +++ b/ood-portal-generator/lib/ood_portal_generator/application.rb @@ -59,6 +59,10 @@ def skip_exit detailed_exitcodes ? 4 : 0 end + def insecure + @insecure.nil? ? false : @insecure + end + def apache ENV['APACHE'] || (OodPortalGenerator.scl_apache? ? '/opt/rh/httpd24/root/etc/httpd/conf.d/ood-portal.conf' : '/etc/httpd/conf.d/ood-portal.conf') end @@ -140,7 +144,7 @@ def exit!(code) def generate() view = View.new(context) - dex = Dex.new(context, view) + dex = Dex.new(context, view, insecure) rendered_template = view.render(template.read) output.write(rendered_template) if Dex.installed? && dex.enabled? @@ -307,6 +311,10 @@ def add_shared_opt_parser_attrs(parser) @template = v end + parser.on("-i", "--insecure", TrueClass, "Generate insecure configs if configured") do |v| + @insecure = v + end + parser.on("-v", "--version", "Print current version") do puts "version #{OodPortalGenerator::VERSION}" exit diff --git a/ood-portal-generator/lib/ood_portal_generator/dex.rb b/ood-portal-generator/lib/ood_portal_generator/dex.rb index affbdea182..34874d7eb2 100644 --- a/ood-portal-generator/lib/ood_portal_generator/dex.rb +++ b/ood-portal-generator/lib/ood_portal_generator/dex.rb @@ -2,12 +2,13 @@ require 'active_support/core_ext' require 'securerandom' require 'fileutils' +require 'bcrypt' module OodPortalGenerator # A view class that renders a Dex configuration class Dex # @param opts [#to_h] the options describing the context used to render the Dex config - def initialize(opts = {}, view) + def initialize(opts = {}, view = nil, insecure = false) opts = opts.to_h.deep_symbolize_keys config = opts.fetch(:dex, {}) if config.nil? || config == false @@ -34,14 +35,11 @@ def initialize(opts = {}, view) @dex_config[:staticClients] = static_clients @dex_config[:connectors] = connectors unless connectors.nil? @dex_config[:oauth2] = { skipApprovalScreen: true } - @dex_config[:enablePasswordDB] = connectors.nil? - if connectors.nil? - @dex_config[:staticPasswords] = [{ - email: 'ood@localhost', - hash: '$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W', - username: 'ood', - userID: '08a8684b-db88-4b73-90a9-3cd1661f5466', - }] + if insecure + @dex_config[:enablePasswordDB] = true + @dex_config[:staticPasswords] = static_passwords + else + @dex_config[:enablePasswordDB] = false end @dex_config[:frontend] = frontend # Pass values back to main ood-portal.conf view @@ -195,6 +193,30 @@ def connectors @config.fetch(:connectors, nil) end + def hash_password(password) + BCrypt::Password.create(password).to_s + end + + def static_passwords + passwords = [] + configured = @config.fetch(:static_passwords, nil) + if configured.nil? + default = { + email: 'ood@localhost', + hash: '$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W', + username: 'ood', + userID: '08a8684b-db88-4b73-90a9-3cd1661f5466', + } + passwords << default + else + configured.each do |conf| + conf[:hash] = hash_password(conf[:password]) if conf.key?(:password) + passwords << conf.except(:password) + end + end + passwords + end + def frontend { dir: '/usr/share/ondemand-dex/web', diff --git a/ood-portal-generator/spec/application_spec.rb b/ood-portal-generator/spec/application_spec.rb index 7b86f876dc..d8196e6eb6 100644 --- a/ood-portal-generator/spec/application_spec.rb +++ b/ood-portal-generator/spec/application_spec.rb @@ -156,7 +156,28 @@ described_class.generate() end + it 'generates default dex configs with custom static password' do + allow(described_class).to receive(:insecure).and_return(true) + allow_any_instance_of(OodPortalGenerator::Dex).to receive(:hash_password).with('secret').and_return('$2a$12$iKLecAIN9MrxOZ0UltRb.OQOms/bgQbs5F.qCehq15oc3CvGFYzLy') + allow(described_class).to receive(:context).and_return({ + dex: { + static_passwords: [{ + 'email' => 'username@localhost', + 'password' => 'secret', + 'username' => 'username', + 'userID' => 'D642A38C-402F-47AA-879B-FEC95745F5BA', + }] + }, + }) + expected_rendered = read_fixture('ood-portal.conf.dex') + expect(described_class.output).to receive(:write).with(expected_rendered) + expected_dex_yaml = read_fixture('dex.yaml.static_passwords').gsub('/etc/ood/dex', config_dir) + expect(described_class.dex_output).to receive(:write).with(expected_dex_yaml) + described_class.generate() + end + it 'generates full dex configs with SSL using proxy' do + allow(described_class).to receive(:insecure).and_return(true) allow(described_class).to receive(:context).and_return({ servername: 'example.com', proxy_server: 'example-proxy.com', @@ -175,6 +196,7 @@ end it 'generates full dex configs with SSL' do + allow(described_class).to receive(:insecure).and_return(true) allow(described_class).to receive(:context).and_return({ servername: 'example.com', port: '443', @@ -192,6 +214,7 @@ end it 'generates full dex configs with SSL and multiple redirect URIs' do + allow(described_class).to receive(:insecure).and_return(true) allow(described_class).to receive(:context).and_return({ servername: 'example.com', port: '443', @@ -214,6 +237,7 @@ it 'generates custom dex configs' do with_modified_env CONFIG: 'spec/fixtures/ood_portal.dex.yaml' do + allow(described_class).to receive(:insecure).and_return(true) expected_dex_yaml = read_fixture('dex.custom.yaml').gsub('/etc/ood/dex', config_dir) expect(described_class.dex_output).to receive(:write).with(expected_dex_yaml) described_class.generate() @@ -287,6 +311,7 @@ end it 'generates Dex config using secure secret' do + allow(described_class).to receive(:insecure).and_return(true) secret = Tempfile.new('secret') File.write(secret.path, "supersecret\n") allow(described_class).to receive(:context).and_return({ diff --git a/ood-portal-generator/spec/fixtures/dex.yaml.default b/ood-portal-generator/spec/fixtures/dex.yaml.default index 17018d0e82..51309cd132 100644 --- a/ood-portal-generator/spec/fixtures/dex.yaml.default +++ b/ood-portal-generator/spec/fixtures/dex.yaml.default @@ -16,12 +16,7 @@ staticClients: secret: 83bc78b7-6f5e-4010-9d80-22f328aa6550 oauth2: skipApprovalScreen: true -enablePasswordDB: true -staticPasswords: -- email: ood@localhost - hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" - username: ood - userID: '08a8684b-db88-4b73-90a9-3cd1661f5466' +enablePasswordDB: false frontend: dir: "/usr/share/ondemand-dex/web" theme: ondemand diff --git a/ood-portal-generator/spec/fixtures/dex.yaml.static_passwords b/ood-portal-generator/spec/fixtures/dex.yaml.static_passwords new file mode 100644 index 0000000000..86c2527e61 --- /dev/null +++ b/ood-portal-generator/spec/fixtures/dex.yaml.static_passwords @@ -0,0 +1,27 @@ +--- +issuer: http://example.com:5556 +storage: + type: sqlite3 + config: + file: "/etc/ood/dex/dex.db" +web: + http: 0.0.0.0:5556 +telemetry: + http: 0.0.0.0:5558 +staticClients: +- id: example.com + redirectURIs: + - http://example.com/oidc + name: OnDemand + secret: 83bc78b7-6f5e-4010-9d80-22f328aa6550 +oauth2: + skipApprovalScreen: true +enablePasswordDB: true +staticPasswords: +- email: username@localhost + username: username + userID: D642A38C-402F-47AA-879B-FEC95745F5BA + hash: "$2a$12$iKLecAIN9MrxOZ0UltRb.OQOms/bgQbs5F.qCehq15oc3CvGFYzLy" +frontend: + dir: "/usr/share/ondemand-dex/web" + theme: ondemand diff --git a/packaging/ondemand.spec b/packaging/ondemand.spec index 006a0dc3df..abacebadae 100644 --- a/packaging/ondemand.spec +++ b/packaging/ondemand.spec @@ -125,7 +125,6 @@ set -x set -e export GEM_HOME=$(pwd)/gems-build export GEM_PATH=$(pwd)/gems-build:$GEM_PATH -bundle install rake --trace -mj%{ncpus} build EOS diff --git a/spec/e2e/e2e_helper.rb b/spec/e2e/e2e_helper.rb index 918ff59a94..2367f54119 100644 --- a/spec/e2e/e2e_helper.rb +++ b/spec/e2e/e2e_helper.rb @@ -123,6 +123,9 @@ def install_ondemand end on hosts, "#{config_manager} --save --setopt ondemand-web.exclude='ondemand ondemand-gems* ondemand-selinux'" install_packages(['ondemand', 'ondemand-dex', 'ondemand-selinux']) + # Avoid 'update_ood_portal --rpm' so that --insecure can be used + on hosts, "sed -i 's|--rpm|--rpm --insecure|g' /etc/systemd/system/#{apache_service}.service.d/ood-portal.conf" + on hosts, "systemctl daemon-reload" end end @@ -131,7 +134,7 @@ def upload_portal_config(file) end def update_ood_portal - on hosts, '/opt/ood/ood-portal-generator/sbin/update_ood_portal' + on hosts, '/opt/ood/ood-portal-generator/sbin/update_ood_portal --insecure' end def restart_apache