From c40ca71d9383f756e55a63e5ad2f8b888ce32e45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 20:01:38 +0000 Subject: [PATCH 1/9] Bump rails from 7.2.0 to 7.2.1 Bumps [rails](https://github.com/rails/rails) from 7.2.0 to 7.2.1. - [Release notes](https://github.com/rails/rails/releases) - [Commits](https://github.com/rails/rails/compare/v7.2.0...v7.2.1) --- updated-dependencies: - dependency-name: rails dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Gemfile | 2 +- Gemfile.lock | 110 +++++++++++++++++++++++++-------------------------- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/Gemfile b/Gemfile index a8e16e9..434ae8a 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ source "https://rubygems.org" # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" -gem "rails", "~> 7.2.0" +gem "rails", "~> 7.2.1" # Use sqlite3 as the database for Active Record gem "sqlite3", ">= 1.4" # Use the Puma web server [https://github.com/puma/puma] diff --git a/Gemfile.lock b/Gemfile.lock index aefbe81..1754acb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,29 +1,29 @@ GEM remote: https://rubygems.org/ specs: - actioncable (7.2.0) - actionpack (= 7.2.0) - activesupport (= 7.2.0) + actioncable (7.2.1) + actionpack (= 7.2.1) + activesupport (= 7.2.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.2.0) - actionpack (= 7.2.0) - activejob (= 7.2.0) - activerecord (= 7.2.0) - activestorage (= 7.2.0) - activesupport (= 7.2.0) + actionmailbox (7.2.1) + actionpack (= 7.2.1) + activejob (= 7.2.1) + activerecord (= 7.2.1) + activestorage (= 7.2.1) + activesupport (= 7.2.1) mail (>= 2.8.0) - actionmailer (7.2.0) - actionpack (= 7.2.0) - actionview (= 7.2.0) - activejob (= 7.2.0) - activesupport (= 7.2.0) + actionmailer (7.2.1) + actionpack (= 7.2.1) + actionview (= 7.2.1) + activejob (= 7.2.1) + activesupport (= 7.2.1) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (7.2.0) - actionview (= 7.2.0) - activesupport (= 7.2.0) + actionpack (7.2.1) + actionview (= 7.2.1) + activesupport (= 7.2.1) nokogiri (>= 1.8.5) racc rack (>= 2.2.4, < 3.2) @@ -32,35 +32,35 @@ GEM rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (7.2.0) - actionpack (= 7.2.0) - activerecord (= 7.2.0) - activestorage (= 7.2.0) - activesupport (= 7.2.0) + actiontext (7.2.1) + actionpack (= 7.2.1) + activerecord (= 7.2.1) + activestorage (= 7.2.1) + activesupport (= 7.2.1) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.2.0) - activesupport (= 7.2.0) + actionview (7.2.1) + activesupport (= 7.2.1) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (7.2.0) - activesupport (= 7.2.0) + activejob (7.2.1) + activesupport (= 7.2.1) globalid (>= 0.3.6) - activemodel (7.2.0) - activesupport (= 7.2.0) - activerecord (7.2.0) - activemodel (= 7.2.0) - activesupport (= 7.2.0) + activemodel (7.2.1) + activesupport (= 7.2.1) + activerecord (7.2.1) + activemodel (= 7.2.1) + activesupport (= 7.2.1) timeout (>= 0.4.0) - activestorage (7.2.0) - actionpack (= 7.2.0) - activejob (= 7.2.0) - activerecord (= 7.2.0) - activesupport (= 7.2.0) + activestorage (7.2.1) + actionpack (= 7.2.1) + activejob (= 7.2.1) + activerecord (= 7.2.1) + activesupport (= 7.2.1) marcel (~> 1.0) - activesupport (7.2.0) + activesupport (7.2.1) base64 bigdecimal concurrent-ruby (~> 1.0, >= 1.3.1) @@ -114,7 +114,7 @@ GEM net-smtp marcel (1.0.4) mini_mime (1.1.5) - minitest (5.24.1) + minitest (5.25.1) msgpack (1.7.2) net-imap (0.4.14) date @@ -155,20 +155,20 @@ GEM rackup (2.1.0) rack (>= 3) webrick (~> 1.8) - rails (7.2.0) - actioncable (= 7.2.0) - actionmailbox (= 7.2.0) - actionmailer (= 7.2.0) - actionpack (= 7.2.0) - actiontext (= 7.2.0) - actionview (= 7.2.0) - activejob (= 7.2.0) - activemodel (= 7.2.0) - activerecord (= 7.2.0) - activestorage (= 7.2.0) - activesupport (= 7.2.0) + rails (7.2.1) + actioncable (= 7.2.1) + actionmailbox (= 7.2.1) + actionmailer (= 7.2.1) + actionpack (= 7.2.1) + actiontext (= 7.2.1) + actionview (= 7.2.1) + activejob (= 7.2.1) + activemodel (= 7.2.1) + activerecord (= 7.2.1) + activestorage (= 7.2.1) + activesupport (= 7.2.1) bundler (>= 1.15.0) - railties (= 7.2.0) + railties (= 7.2.1) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest @@ -176,9 +176,9 @@ GEM rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) - railties (7.2.0) - actionpack (= 7.2.0) - activesupport (= 7.2.0) + railties (7.2.1) + actionpack (= 7.2.1) + activesupport (= 7.2.1) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) @@ -272,7 +272,7 @@ DEPENDENCIES debug jwt puma (>= 5.0) - rails (~> 7.2.0) + rails (~> 7.2.1) rubocop-rails-omakase sqlite3 (>= 1.4) tzinfo-data From c3ab3a59df24dce3c5d3397ae7d19ff36227251e Mon Sep 17 00:00:00 2001 From: Geoffrey Wilson Date: Fri, 23 Aug 2024 17:20:39 -0400 Subject: [PATCH 2/9] replace app_registry with domain_ownership_service --- app/lib/services/app_registry_service.rb | 32 -------------------- app/lib/services/auth_service.rb | 24 ++++++++++++--- app/lib/services/domain_ownership_service.rb | 19 ++++++++++++ app/models/domain_info.rb | 8 +++++ app/models/identity.rb | 7 +++++ 5 files changed, 53 insertions(+), 37 deletions(-) delete mode 100644 app/lib/services/app_registry_service.rb create mode 100644 app/lib/services/domain_ownership_service.rb create mode 100644 app/models/domain_info.rb create mode 100644 app/models/identity.rb diff --git a/app/lib/services/app_registry_service.rb b/app/lib/services/app_registry_service.rb deleted file mode 100644 index 6b92cc2..0000000 --- a/app/lib/services/app_registry_service.rb +++ /dev/null @@ -1,32 +0,0 @@ -module Services - class AppRegistryService - def authenticate!(token) - identity = decode(token) - raise AuthError unless identity - # TODO verify identity with authority? - identity - end - - def authorize!(identity, cert_req) - cert_req.fqdns.each do |fqdn| - domain = get_domain_name(fqdn) - raise AuthError unless (domain[:auto_approved_groups] & identity[:groups]).any? - end - end - - private - - def decode(token) - # Decode a JWT access token using the configured base. - body = JWT.decode(token, Rails.application.config.astral[:jwt_signing_key])[0] - HashWithIndifferentAccess.new body - rescue => e - Rails.logger.warn "Unable to decode token: #{e}" - nil - end - - def get_domain_name(fqdn) - # TODO implement - end - end -end diff --git a/app/lib/services/auth_service.rb b/app/lib/services/auth_service.rb index a1e1764..2d34ec6 100644 --- a/app/lib/services/auth_service.rb +++ b/app/lib/services/auth_service.rb @@ -1,16 +1,30 @@ module Services class AuthService def initialize - # TODO make this selectable - @impl = AppRegistryService.new + @domain_ownership_service = DomainOwnershipService.new end def authenticate!(token) - @impl.authenticate!(token) + identity = decode(token) + raise AuthError unless identity + # TODO verify identity with authority? + identity end - def authorize!(token, cert_issue_req) - @impl.authorize!(token, cert_issue_req) + def authorize!(identity, cert_issue_req) + @domain_ownership_service.authorize!(identity, cert_issue_req) end + + private + + def decode(token) + # Decode a JWT access token using the configured base. + body = JWT.decode(token, Rails.application.config.astral[:jwt_signing_key])[0] + Identity.new(body) + rescue => e + Rails.logger.warn "Unable to decode token: #{e}" + nil + end + end end diff --git a/app/lib/services/domain_ownership_service.rb b/app/lib/services/domain_ownership_service.rb new file mode 100644 index 0000000..e31bca1 --- /dev/null +++ b/app/lib/services/domain_ownership_service.rb @@ -0,0 +1,19 @@ +module Services + class DomainOwnershipService + + def authorize!(identity, cert_req) + cert_req.fqdns.each do |fqdn| + domain = get_domain_name(fqdn) + raise AuthError unless (domain.owner == identity.subject || + (domain.group_delegation && + (domain.groups & identity.groups).any?)) + end + end + + private + + def get_domain_name(fqdn) + # TODO implement + end + end +end diff --git a/app/models/domain_info.rb b/app/models/domain_info.rb new file mode 100644 index 0000000..f176757 --- /dev/null +++ b/app/models/domain_info.rb @@ -0,0 +1,8 @@ +class DomainInfo + include ActiveModel::Model + include ActiveModel::Attributes + + attribute :owner, :string + attribute :groups, array: :string, default: [] + attribute :group_delegation, :bool, default: false +end diff --git a/app/models/identity.rb b/app/models/identity.rb new file mode 100644 index 0000000..feda49e --- /dev/null +++ b/app/models/identity.rb @@ -0,0 +1,7 @@ +class Identity + include ActiveModel::Model + include ActiveModel::Attributes + + attribute :subject, :string + attribute :groups, array: :string, default: [] +end From 4cadf7fcf3f024b239f01a86a2a6f498ef07eb8a Mon Sep 17 00:00:00 2001 From: Geoff Wilson Date: Fri, 23 Aug 2024 17:54:49 -0400 Subject: [PATCH 3/9] fix test for decoding token to model --- README.md | 4 +++- app/models/identity.rb | 6 ++++++ test/integration/certificates_controller_test.rb | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a28d48c..335d513 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ rails s 3) POST /certificates to acquire cert in terminal: ``` curl -X POST http://localhost:3000/certificates \ --H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhcHBsaWNhdGlvbl9uYW1lIiwiY29tbW9uX25hbWUiOiJleGFtcGxlLmNvbSIsImlwX3NhbnMiOiIxMC4wLjEuMTAwIn0.61e0oQIj7vwGtOpFuPJDCI_Bqf8ZTpJxe_2kUwcbN7Y" +-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqb2huLmRvZUBleGFtcGxlLmNvbSIsIm5hbWUiOiJKb2huIERvZSIsImlhdCI6MTUxNjIzOTAyMiwiZ3JvdXBzIjpbImdyb3VwMSIsImdyb3VwMiJdLCJhdWQiOiJhc3RyYWwifQ.tfRLXmE_eq-piP88_clwPWrYfMAQbCJAeZQI6OFxZSI" \ +-H "Content-type: application/json" \ +-d "{ \"common_name\": \"example.com\" }" ``` diff --git a/app/models/identity.rb b/app/models/identity.rb index feda49e..20c7491 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -3,5 +3,11 @@ class Identity include ActiveModel::Attributes attribute :subject, :string + attribute :name, :string + attribute :iat, :integer + attribute :aud, :string attribute :groups, array: :string, default: [] + + alias_attribute :sub, :subject + alias_attribute :roles, :groups end diff --git a/test/integration/certificates_controller_test.rb b/test/integration/certificates_controller_test.rb index 3df4e5e..c22962d 100644 --- a/test/integration/certificates_controller_test.rb +++ b/test/integration/certificates_controller_test.rb @@ -13,7 +13,7 @@ class CertificatesControllerTest < ActionDispatch::IntegrationTest end test "create authorized" do - jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhcHBsaWNhdGlvbl9uYW1lIiwiY29tbW9uX25hbWUiOiJleGFtcGxlLmNvbSIsImlwX3NhbnMiOiIxMC4wLjEuMTAwIn0.61e0oQIj7vwGtOpFuPJDCI_Bqf8ZTpJxe_2kUwcbN7Y" + jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqb2huLmRvZUBleGFtcGxlLmNvbSIsIm5hbWUiOiJKb2huIERvZSIsImlhdCI6MTUxNjIzOTAyMiwiZ3JvdXBzIjpbImdyb3VwMSIsImdyb3VwMiJdLCJhdWQiOiJhc3RyYWwifQ.tfRLXmE_eq-piP88_clwPWrYfMAQbCJAeZQI6OFxZSI" post certificates_path, headers: { "Authorization" => "Bearer #{jwt}" }, params: { common_name: "example.com" } assert_response :success From 2f9d3b0e1f2797759e0cffc1768cc8e216f503d3 Mon Sep 17 00:00:00 2001 From: Geoff Wilson Date: Sat, 24 Aug 2024 16:08:09 -0400 Subject: [PATCH 4/9] Added test for domain_ownership logic --- app/lib/services/domain_ownership_service.rb | 1 + app/models/domain_info.rb | 2 +- .../services/domain_ownership_service_test.rb | 46 +++++++++++++++++++ test/models/cert_isssue_request_test.rb | 1 - test/test_helper.rb | 2 + 5 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 test/lib/services/domain_ownership_service_test.rb diff --git a/app/lib/services/domain_ownership_service.rb b/app/lib/services/domain_ownership_service.rb index e31bca1..16b1a12 100644 --- a/app/lib/services/domain_ownership_service.rb +++ b/app/lib/services/domain_ownership_service.rb @@ -8,6 +8,7 @@ def authorize!(identity, cert_req) (domain.group_delegation && (domain.groups & identity.groups).any?)) end + nil end private diff --git a/app/models/domain_info.rb b/app/models/domain_info.rb index f176757..ce724d5 100644 --- a/app/models/domain_info.rb +++ b/app/models/domain_info.rb @@ -4,5 +4,5 @@ class DomainInfo attribute :owner, :string attribute :groups, array: :string, default: [] - attribute :group_delegation, :bool, default: false + attribute :group_delegation, :boolean, default: false end diff --git a/test/lib/services/domain_ownership_service_test.rb b/test/lib/services/domain_ownership_service_test.rb new file mode 100644 index 0000000..1568f7a --- /dev/null +++ b/test/lib/services/domain_ownership_service_test.rb @@ -0,0 +1,46 @@ +require "test_helper" + +class DomainOwnershipServiceTest < ActiveSupport::TestCase + def setup + @identity = Identity.new(subject: "test@example.com", groups: ["admin_group"]) + @domain = DomainInfo.new(owner: "test@example.com", group_delegation: false, groups: ["admin_group"]) + end + + test "#authorize! with matching owner" do + ds = Services::DomainOwnershipService.new + ds.stub :get_domain_name, @domain do + assert_nil(ds.authorize!(@identity, CertIssueRequest.new)) + end + end + + test "#authorize! with non-matching owner" do + ds = Services::DomainOwnershipService.new + @domain.owner = "different_owner@example.com" + ds.stub :get_domain_name, @domain do + assert_raises(AuthError) do + ds.authorize!(@identity, CertIssueRequest.new) + end + end + end + + test "#authorize! with matching group" do + ds = Services::DomainOwnershipService.new + @domain.owner = "different_owner@example.com" + @domain.group_delegation = true + ds.stub :get_domain_name, @domain do + assert_nil(ds.authorize!(@identity, CertIssueRequest.new)) + end + end + + test "#authorize! with non-matching group" do + ds = Services::DomainOwnershipService.new + @domain.owner = "different_owner@example.com" + @identity.groups = ["different_group"] + ds.stub :get_domain_name, @domain do + assert_raises(AuthError) do + ds.authorize!(@identity, CertIssueRequest.new) + end + end + end + +end diff --git a/test/models/cert_isssue_request_test.rb b/test/models/cert_isssue_request_test.rb index 8738915..060ef95 100644 --- a/test/models/cert_isssue_request_test.rb +++ b/test/models/cert_isssue_request_test.rb @@ -70,7 +70,6 @@ def setup assert_not @cert_issue_request.valid? end - test "fqdns should return alt_names plus common_name" do assert_equal [ "alt1.example.com", "alt2.example.com", "example.com" ], @cert_issue_request.fqdns end diff --git a/test/test_helper.rb b/test/test_helper.rb index 0c22470..c89aa89 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,6 +1,8 @@ ENV["RAILS_ENV"] ||= "test" require_relative "../config/environment" require "rails/test_help" +require "minitest" +require "minitest/mock" module ActiveSupport class TestCase From c34c1ab7f4b67cfb1705bc3cfb9c5514c84c1ebd Mon Sep 17 00:00:00 2001 From: Geoff Wilson Date: Mon, 26 Aug 2024 11:16:08 -0400 Subject: [PATCH 5/9] better test descriptions --- app/lib/services/domain_ownership_service.rb | 5 ++--- test/integration/certificates_controller_test.rb | 6 +++--- test/models/cert_isssue_request_test.rb | 16 ++++++++-------- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/app/lib/services/domain_ownership_service.rb b/app/lib/services/domain_ownership_service.rb index 16b1a12..69ee9c6 100644 --- a/app/lib/services/domain_ownership_service.rb +++ b/app/lib/services/domain_ownership_service.rb @@ -1,12 +1,11 @@ module Services class DomainOwnershipService - def authorize!(identity, cert_req) cert_req.fqdns.each do |fqdn| domain = get_domain_name(fqdn) - raise AuthError unless (domain.owner == identity.subject || + raise AuthError unless domain.owner == identity.subject || (domain.group_delegation && - (domain.groups & identity.groups).any?)) + (domain.groups & identity.groups).any?) end nil end diff --git a/test/integration/certificates_controller_test.rb b/test/integration/certificates_controller_test.rb index c22962d..b21e156 100644 --- a/test/integration/certificates_controller_test.rb +++ b/test/integration/certificates_controller_test.rb @@ -1,18 +1,18 @@ require "test_helper" class CertificatesControllerTest < ActionDispatch::IntegrationTest - test "create unauthorized" do + test "#create unauthorized" do post certificates_path assert_response :unauthorized end - test "create with faulty token (encoded with different signing key)" do + test "#create with faulty token (encoded with different signing key)" do jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhcHBsaWNhdGlvbl9uYW1lIiwiY29tbW9uX25hbWUiOiJleGFtcGxlLmNvbSIsImlwX3NhbnMiOiIxMC4wLjEuMTAwIn0.gEUyaZcARiBQNq2RUwZU0MdFXqthyo_oSQ8DAgKvxCs" post certificates_path, headers: { "Authorization" => "Bearer #{jwt}" } assert_response :unauthorized end - test "create authorized" do + test "#create authorized" do jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqb2huLmRvZUBleGFtcGxlLmNvbSIsIm5hbWUiOiJKb2huIERvZSIsImlhdCI6MTUxNjIzOTAyMiwiZ3JvdXBzIjpbImdyb3VwMSIsImdyb3VwMiJdLCJhdWQiOiJhc3RyYWwifQ.tfRLXmE_eq-piP88_clwPWrYfMAQbCJAeZQI6OFxZSI" post certificates_path, headers: { "Authorization" => "Bearer #{jwt}" }, params: { common_name: "example.com" } diff --git a/test/models/cert_isssue_request_test.rb b/test/models/cert_isssue_request_test.rb index 060ef95..5ab1363 100644 --- a/test/models/cert_isssue_request_test.rb +++ b/test/models/cert_isssue_request_test.rb @@ -24,35 +24,35 @@ def setup @cert_issue_request = CertIssueRequest.new(@attributes) end - test "should set attributes correctly" do + test "#new should set attributes from attributes argument" do @attributes.each do |key, value| assert_equal value, @cert_issue_request.send(key), "Attribute #{key} was not set correctly" end end - test "should be valid with valid attributes" do + test "#valid? should be valid with valid attributes" do assert @cert_issue_request.valid? end - test "should require a common_name" do + test "#valid? should require a common_name" do @cert_issue_request.common_name = nil assert_not @cert_issue_request.valid? assert_includes @cert_issue_request.errors[:common_name], "can't be blank" end - test "should require a valid format" do + test "#valid? should require a valid format" do @cert_issue_request.format = "invalid_format" assert_not @cert_issue_request.valid? assert_includes @cert_issue_request.errors[:format], "is not included in the list" end - test "should require a valid private_key_format" do + test "#valid? should require a valid private_key_format" do @cert_issue_request.private_key_format = "invalid_format" assert_not @cert_issue_request.valid? assert_includes @cert_issue_request.errors[:private_key_format], "is not included in the list" end - test "should have default values" do + test "#new should have default values" do @cert_issue_request = CertIssueRequest.new assert_equal false, @cert_issue_request.exclude_cn_from_sans assert_equal "pem", @cert_issue_request.format @@ -65,12 +65,12 @@ def setup assert_equal true, @cert_issue_request.server_flag end - test "should be invalid with default values" do + test "#valid? should be false with default values" do @cert_issue_request = CertIssueRequest.new assert_not @cert_issue_request.valid? end - test "fqdns should return alt_names plus common_name" do + test "#fqdns should return alt_names plus common_name" do assert_equal [ "alt1.example.com", "alt2.example.com", "example.com" ], @cert_issue_request.fqdns end end From 12399b7475500ae81952e0b9d4fb262cfbc4d79e Mon Sep 17 00:00:00 2001 From: Geoff Wilson Date: Mon, 26 Aug 2024 14:13:24 -0400 Subject: [PATCH 6/9] fix lint complaints --- app/lib/services/auth_service.rb | 1 - test/lib/services/domain_ownership_service_test.rb | 7 +++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/lib/services/auth_service.rb b/app/lib/services/auth_service.rb index 2d34ec6..2d1bd7b 100644 --- a/app/lib/services/auth_service.rb +++ b/app/lib/services/auth_service.rb @@ -25,6 +25,5 @@ def decode(token) Rails.logger.warn "Unable to decode token: #{e}" nil end - end end diff --git a/test/lib/services/domain_ownership_service_test.rb b/test/lib/services/domain_ownership_service_test.rb index 1568f7a..3abe508 100644 --- a/test/lib/services/domain_ownership_service_test.rb +++ b/test/lib/services/domain_ownership_service_test.rb @@ -2,8 +2,8 @@ class DomainOwnershipServiceTest < ActiveSupport::TestCase def setup - @identity = Identity.new(subject: "test@example.com", groups: ["admin_group"]) - @domain = DomainInfo.new(owner: "test@example.com", group_delegation: false, groups: ["admin_group"]) + @identity = Identity.new(subject: "test@example.com", groups: [ "admin_group" ]) + @domain = DomainInfo.new(owner: "test@example.com", group_delegation: false, groups: [ "admin_group" ]) end test "#authorize! with matching owner" do @@ -35,12 +35,11 @@ def setup test "#authorize! with non-matching group" do ds = Services::DomainOwnershipService.new @domain.owner = "different_owner@example.com" - @identity.groups = ["different_group"] + @identity.groups = [ "different_group" ] ds.stub :get_domain_name, @domain do assert_raises(AuthError) do ds.authorize!(@identity, CertIssueRequest.new) end end end - end From 946865cc3717a1ec2f061e2a8a391dd24c3f0a65 Mon Sep 17 00:00:00 2001 From: Geoff Wilson Date: Wed, 28 Aug 2024 11:58:16 -0400 Subject: [PATCH 7/9] New release Dockerfile using ruby-alpine --- Dockerfile | 68 ++++++++++++++++++++---------------------------------- 1 file changed, 25 insertions(+), 43 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9713604..298fef6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,66 +1,48 @@ # syntax = docker/dockerfile:1 +FROM ruby:3.3.4-alpine AS base -# This Dockerfile is designed for production, not development. Use with Kamal or build'n'run by hand: -# docker build -t my-app . -# docker run -d -p 80:80 -p 443:443 --name my-app -e RAILS_MASTER_KEY= my-app +# Install build dependencies +RUN apk add --no-cache build-base git pkgconfig -# Make sure RUBY_VERSION matches the Ruby version in .ruby-version -ARG RUBY_VERSION=3.3.4 -FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base - -# Rails app lives here -WORKDIR /rails - -# Install base packages -RUN apt-get update -qq && \ - apt-get install --no-install-recommends -y curl libjemalloc2 libsqlite3-0 libvips && \ - rm -rf /var/lib/apt/lists /var/cache/apt/archives +WORKDIR /app # Set production environment ENV RAILS_ENV="production" \ BUNDLE_DEPLOYMENT="1" \ BUNDLE_PATH="/usr/local/bundle" \ - BUNDLE_WITHOUT="development" + BUNDLE_WITHOUT="test development" -# Throw-away build stage to reduce size of final image -FROM base AS build +FROM base AS builder -# Install packages needed to build gems -RUN apt-get update -qq && \ - apt-get install --no-install-recommends -y build-essential git pkg-config && \ - rm -rf /var/lib/apt/lists /var/cache/apt/archives - -# Install application gems +# Install gems COPY Gemfile Gemfile.lock ./ -RUN bundle install && \ - rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \ +RUN bundle install --jobs 4 --retry 3 && \ + rm -rf ~/.bundle/ $BUNDLE_PATH/ruby/*/cache \ + $BUNDLE_PATH/ruby/*/bundler/gems/*/.git && \ bundle exec bootsnap precompile --gemfile - + # Copy application code COPY . . -# Precompile bootsnap code for faster boot times +# Precompile bootsnap RUN bundle exec bootsnap precompile app/ lib/ +# Final stage +FROM base +# Install runtime dependencies +RUN apk add --no-cache curl jemalloc sqlite-libs vips tzdata +WORKDIR /app -# Final stage for app image -FROM base - -# Copy built artifacts: gems, application -COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}" -COPY --from=build /rails /rails +# Copy built artifacts +COPY --from=builder /usr/local/bundle /usr/local/bundle +COPY --from=builder /app /app -# Run and own only the runtime files as a non-root user for security -RUN groupadd --system --gid 1000 rails && \ - useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \ +# Add non-root user +RUN addgroup -S rails && adduser -S rails -G rails && \ chown -R rails:rails db log storage tmp -USER 1000:1000 - -# Entrypoint prepares the database. -ENTRYPOINT ["/rails/bin/docker-entrypoint"] +USER rails:rails -# Start the server by default, this can be overwritten at runtime -EXPOSE 3000 -CMD ["./bin/rails", "server"] +# Start the server +CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"] \ No newline at end of file From 2e618dd5323f3ad02da7e5b81580976f5eec51f4 Mon Sep 17 00:00:00 2001 From: Geoff Wilson Date: Wed, 28 Aug 2024 14:28:07 -0400 Subject: [PATCH 8/9] A few more tweaks for prod/release Dockerfile --- Dockerfile | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/Dockerfile b/Dockerfile index 298fef6..14dc515 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,26 +1,29 @@ # syntax = docker/dockerfile:1 -FROM ruby:3.3.4-alpine AS base +ARG RUBY_VERSION=3.3.4 +ARG RAILS_ROOT=/app +FROM ruby:$RUBY_VERSION-alpine AS builder # Install build dependencies RUN apk add --no-cache build-base git pkgconfig -WORKDIR /app - # Set production environment ENV RAILS_ENV="production" \ + RAILS_ROOT="/app" \ BUNDLE_DEPLOYMENT="1" \ - BUNDLE_PATH="/usr/local/bundle" \ + BUNDLE_PATH="/app/.bundle" \ BUNDLE_WITHOUT="test development" -FROM base AS builder +WORKDIR $RAILS_ROOT # Install gems COPY Gemfile Gemfile.lock ./ -RUN bundle install --jobs 4 --retry 3 && \ - rm -rf ~/.bundle/ $BUNDLE_PATH/ruby/*/cache \ - $BUNDLE_PATH/ruby/*/bundler/gems/*/.git && \ - bundle exec bootsnap precompile --gemfile - +RUN bundle config --global frozen 1 \ + && bundle config set path 'vendor/bundle' \ + && bundle install --without development:test -j4 --retry 3 \ + && rm -rf vendor/bundle/ruby/3.3.0/cache/*.gem # \ + && find vendor/bundle/ruby/3.3.0/gems/ -name "*.c" -delete \ + && find vendor/bundle/ruby/3.3.0/gems/ -name "*.o" -delete + # Copy application code COPY . . @@ -28,21 +31,28 @@ COPY . . RUN bundle exec bootsnap precompile app/ lib/ # Final stage -FROM base +FROM ruby:$RUBY_VERSION-alpine # Install runtime dependencies RUN apk add --no-cache curl jemalloc sqlite-libs vips tzdata -WORKDIR /app +ENV RAILS_ENV="production" \ + RAILS_ROOT="/app" \ + BUNDLE_DEPLOYMENT="1" \ + BUNDLE_APP_CONFIG="/app/.bundle" \ + BUNDLE_WITHOUT="test development" + +WORKDIR $RAILS_ROOT # Copy built artifacts -COPY --from=builder /usr/local/bundle /usr/local/bundle -COPY --from=builder /app /app +COPY --from=builder $RAILS_ROOT $RAILS_ROOT # Add non-root user -RUN addgroup -S rails && adduser -S rails -G rails && \ - chown -R rails:rails db log storage tmp +RUN addgroup -S rails && adduser -S rails -G rails # && \ + # chown -R rails:rails db log storage tmp USER rails:rails +# Start the server by default, this can be overwritten at runtime +EXPOSE 3000 # Start the server -CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"] \ No newline at end of file +CMD ["bin/rails", "server", "-b", "0.0.0.0"] \ No newline at end of file From fc6baa06c1350cfc51a824601744f7096929ad08 Mon Sep 17 00:00:00 2001 From: Geoff Wilson Date: Wed, 28 Aug 2024 14:33:44 -0400 Subject: [PATCH 9/9] Fix the chown for rails user --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 14dc515..e5ec729 100644 --- a/Dockerfile +++ b/Dockerfile @@ -48,8 +48,8 @@ WORKDIR $RAILS_ROOT COPY --from=builder $RAILS_ROOT $RAILS_ROOT # Add non-root user -RUN addgroup -S rails && adduser -S rails -G rails # && \ - # chown -R rails:rails db log storage tmp +RUN addgroup -S rails && adduser -S rails -G rails && \ + chown -R rails:rails db log storage tmp USER rails:rails # Start the server by default, this can be overwritten at runtime