Skip to content

Commit 6b776c4

Browse files
authored
Merge pull request #589 from 3scale/backport-12122-2.16-que-worker-tls
THREESCALE-12122: Fix Que worker connection to TLS protected DB [2.16 backport]
2 parents 89c220c + d28cc45 commit 6b776c4

5 files changed

Lines changed: 509 additions & 32 deletions

File tree

.circleci/config.yml

Lines changed: 122 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,44 @@
11
version: 2.1
2+
3+
commands:
4+
bundle_install:
5+
steps:
6+
- run:
7+
name: bundle install
8+
command: |
9+
bundle config set --local force_ruby_platform true
10+
bundle config set --local deployment 'true'
11+
bundle config set --local path 'vendor/bundle'
12+
bundle install --jobs $(grep -c processor /proc/cpuinfo) --retry 3
13+
14+
boot_zync:
15+
steps:
16+
- run:
17+
name: boot zync
18+
command: |
19+
BUNDLE_WITHOUT=development:test bundle exec bin/rails runner --environment=production 'puts Rails.env'
20+
21+
setup_db:
22+
steps:
23+
- run:
24+
name: Set up the DB
25+
command: |
26+
bundle exec bin/rails db:wait db:setup
27+
28+
run_tests:
29+
steps:
30+
- run:
31+
name: rails test
32+
command: |
33+
circleci tests glob "test/**/*_test.rb" | circleci tests run --command="xargs bundle exec rake test TESTOPTS='-v'" --verbose --split-by=timings
34+
35+
run_license_finder:
36+
steps:
37+
- run:
38+
name: license_finder
39+
command: |
40+
bundle exec license_finder
41+
242
jobs:
343
docker-build:
444
resource_class: small
@@ -12,6 +52,7 @@ jobs:
1252
POSTGRES_PASSWORD: postgres
1353
POSTGRES_DB: zync
1454
RAILS_ENV: production
55+
SECRET_KEY_BASE: test
1556
steps:
1657
- checkout
1758
- setup_remote_docker
@@ -20,8 +61,8 @@ jobs:
2061
- run: docker build --tag zync:build --file ./Dockerfile .
2162
- run:
2263
command: |
23-
docker run --net net0 -e RAILS_ENV=${RAILS_ENV} -e DATABASE_URL=${DATABASE_URL} \
24-
zync:build rails db:setup
64+
docker run --net net0 -e RAILS_ENV=${RAILS_ENV} -e DATABASE_URL=${DATABASE_URL} -e SECRET_KEY_BASE=${SECRET_KEY_BASE} \
65+
zync:build bundle exec rails db:setup
2566
2667
build:
2768
parameters:
@@ -36,51 +77,98 @@ jobs:
3677
RAILS_ENV: test
3778
DISABLE_SPRING: 1 # we can't really run spring as it hangs on local circleci build
3879
DATABASE_URL: postgres://postgres:@localhost/circle_test
80+
SECRET_KEY_BASE: test
3981
steps:
4082
- checkout
41-
42-
# Restore bundle cache
4383
- restore_cache:
4484
keys:
4585
- zync-bundle-v2-{{ .Environment.CACHE_VERSION }}-{{ arch }}-{{ checksum "Gemfile.lock" }}
46-
47-
- run:
48-
name: bundle install
49-
command: |
50-
gem install bundler --version=$(grep -A 1 "BUNDLED WITH" Gemfile.lock | tr -d ' '| tail -n 1) --no-document
51-
bundle config --local force_ruby_platform true
52-
bundle config set --local deployment 'true'
53-
bundle config set --local path 'vendor/bundle'
54-
bundle install --jobs $(grep -c processor /proc/cpuinfo) --retry 3
55-
- run:
56-
name: boot zync
57-
command: BUNDLE_WITHOUT=development:test bundle exec bin/rails runner --environment=production 'puts Rails.env'
58-
86+
- bundle_install
5987
- save_cache:
6088
key: zync-bundle-v2-{{ .Environment.CACHE_VERSION }}-{{ arch }}-{{ checksum "Gemfile.lock" }}
6189
paths:
6290
- vendor/bundle
91+
- boot_zync
92+
- setup_db
93+
- run_tests
94+
- run_license_finder
95+
- store_test_results:
96+
path: test/reports
6397

98+
build_ssl:
99+
parameters:
100+
postgresql_image:
101+
type: string
102+
working_directory: /opt/app-root/zync
103+
docker:
104+
- image: quay.io/3scale/zync:ci-builder
105+
- image: << parameters.postgresql_image >>
106+
command:
107+
- bash
108+
- -c
109+
- |
110+
openssl req -nodes -new -x509 -subj "/CN=localhost" -keyout /server.key -out /server.crt -days 365 2>/dev/null
111+
chown postgres:postgres /server.key /server.crt
112+
chmod 600 /server.key
113+
# Configure pg_hba.conf to only allow SSL connections (hostssl instead of host)
114+
# local rule is needed for postgres internal initialization via unix socket
115+
echo "local all all trust" > /tmp/pg_hba.conf
116+
echo "hostssl all all all trust" >> /tmp/pg_hba.conf
117+
exec docker-entrypoint.sh postgres -c ssl=on -c ssl_cert_file=/server.crt -c ssl_key_file=/server.key -c hba_file=/tmp/pg_hba.conf
118+
environment:
119+
POSTGRES_HOST_AUTH_METHOD: trust
120+
POSTGRES_DB: circle_test
121+
environment:
122+
RAILS_ENV: test
123+
DISABLE_SPRING: 1
124+
DATABASE_URL: postgres://postgres:@localhost/circle_test
125+
DATABASE_SSL_MODE: verify-full
126+
SECRET_KEY_BASE: test
127+
steps:
128+
- checkout
64129
- run:
65-
name: Set up the DB
66-
command: bundle exec bin/rails db:wait db:setup
67-
130+
name: Wait for PostgreSQL to be ready
131+
command: |
132+
for i in $(seq 1 30); do
133+
if openssl s_client -starttls postgres -connect localhost:5432 </dev/null 2>&1 | grep -q "SSL handshake"; then
134+
echo "PostgreSQL SSL is ready"
135+
exit 0
136+
fi
137+
echo "Waiting for PostgreSQL SSL... ($i/30)"
138+
sleep 1
139+
done
140+
echo "PostgreSQL failed to start with SSL"
141+
exit 1
68142
- run:
69-
name: rails test
143+
name: Extract server CA certificate and generate client certificate
70144
command: |
71-
circleci tests glob "test/**/*_test.rb" | circleci tests run --command="xargs bundle exec rake test TESTOPTS='-v'" --verbose --split-by=timings
145+
mkdir -p .ssl
146+
# Extract the server certificate (CA) using openssl s_client
147+
openssl s_client -starttls postgres -connect localhost:5432 -showcerts </dev/null 2>/dev/null \
148+
| openssl x509 -outform PEM > .ssl/ca.crt
149+
# Generate client key and certificate (self-signed, PostgreSQL doesn't verify client certs by default)
150+
openssl req -nodes -new -x509 -subj "/CN=postgres" -keyout .ssl/client.key -out .ssl/client.crt -days 365 2>/dev/null
151+
chmod 600 .ssl/client.key
72152
- run:
73-
name: license_finder
153+
name: Set SSL environment variables
74154
command: |
75-
bundle exec license_finder
76-
77-
- store_test_results:
78-
path: test/reports
79-
155+
echo 'export DATABASE_SSL_CA=/opt/app-root/zync/.ssl/ca.crt' >> "$BASH_ENV"
156+
echo 'export DATABASE_SSL_CERT=/opt/app-root/zync/.ssl/client.crt' >> "$BASH_ENV"
157+
echo 'export DATABASE_SSL_KEY=/opt/app-root/zync/.ssl/client.key' >> "$BASH_ENV"
158+
- restore_cache:
159+
keys:
160+
- zync-bundle-v2-{{ .Environment.CACHE_VERSION }}-{{ arch }}-{{ checksum "Gemfile.lock" }}
161+
- bundle_install
80162
- save_cache:
81-
key: zync-branch-v2-{{ arch }}-{{ .Branch }}
163+
key: zync-bundle-v2-{{ .Environment.CACHE_VERSION }}-{{ arch }}-{{ checksum "Gemfile.lock" }}
82164
paths:
83165
- vendor/bundle
166+
- boot_zync
167+
- setup_db
168+
- run_tests
169+
- run_license_finder
170+
- store_test_results:
171+
path: test/reports
84172

85173
workflows:
86174
version: 2.1
@@ -89,5 +177,9 @@ workflows:
89177
- build:
90178
matrix:
91179
parameters:
92-
postgresql_image: [ "cimg/postgres:10.22", "cimg/postgres:12.15", "cimg/postgres:13.11", "cimg/postgres:14.12" ]
180+
postgresql_image: [ "cimg/postgres:14.19", "cimg/postgres:15.14", "cimg/postgres:16.10", "cimg/postgres:17.6", "cimg/postgres:18.0" ]
181+
- build_ssl:
182+
matrix:
183+
parameters:
184+
postgresql_image: [ "cimg/postgres:14.19", "cimg/postgres:15.14", "cimg/postgres:16.10", "cimg/postgres:17.6", "cimg/postgres:18.0" ]
93185
- docker-build

config/initializers/que.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,17 @@
1515

1616
def Que.start!
1717
require 'que/locker'
18+
require 'que/db_connection_url'
1819

1920
# Workaround for https://github.com/chanks/que/pull/192
2021
require 'active_record/base'
21-
Que.locker = Que::Locker.new(**Rails.application.config.x.que)
22+
23+
# Build connection URL with SSL parameters from database config
24+
# Workaround for https://github.com/que-rb/que/issues/442
25+
db_config = ActiveRecord::Base.connection_db_config.configuration_hash
26+
connection_url = Que::DBConnectionURL.build_connection_url(db_config)
27+
28+
Que.locker = Que::Locker.new(connection_url: connection_url, **Rails.application.config.x.que)
2229
end
2330

2431
def Que.stop!

lib/que/db_connection_url.rb

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# frozen_string_literal: true
2+
3+
module Que
4+
class DBConnectionURL
5+
def self.build_connection_url(db_config)
6+
# Build base URL
7+
# Workaround for https://github.com/que-rb/que/issues/442
8+
connection_url = "#{db_config[:adapter]}://"
9+
10+
host = db_config[:host]
11+
is_unix_socket = host && host.start_with?('/')
12+
is_ipv6 = host && !is_unix_socket && host.include?(':')
13+
14+
# Add username and password
15+
connection_url += "#{db_config[:username]}" if db_config[:username]
16+
connection_url += ":#{db_config[:password]}" if db_config[:password]
17+
connection_url += "@" if db_config[:username] || db_config[:password]
18+
19+
# For Unix sockets, leave host empty; for TCP, add host and port
20+
if is_unix_socket
21+
connection_url += "/"
22+
else
23+
# Wrap IPv6 addresses in square brackets
24+
formatted_host = is_ipv6 ? "[#{host}]" : (host || 'localhost')
25+
connection_url += formatted_host
26+
connection_url += ":#{db_config[:port]}" if db_config[:port]
27+
connection_url += "/"
28+
end
29+
30+
connection_url += db_config[:database]
31+
32+
# Build query parameters
33+
params = []
34+
35+
# For Unix sockets, add host and port as query parameters
36+
if is_unix_socket
37+
params << "host=#{host}"
38+
params << "port=#{db_config[:port]}" if db_config[:port]
39+
end
40+
41+
# Add SSL parameters
42+
params << "sslmode=#{db_config[:sslmode]}" if db_config[:sslmode]
43+
params << "sslrootcert=#{db_config[:sslrootcert]}" if db_config[:sslrootcert]
44+
params << "sslcert=#{db_config[:sslcert]}" if db_config[:sslcert]
45+
params << "sslkey=#{db_config[:sslkey]}" if db_config[:sslkey]
46+
47+
connection_url += "?#{params.join('&')}" if params.any?
48+
49+
connection_url
50+
end
51+
end
52+
end

lib/tasks/que.rake

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
# frozen_string_literal: true
22

3+
require 'que/db_connection_url'
4+
35
task que: 'que:exec'
46

57
namespace :que do
68
desc 'Start que worker'
79
task exec: :environment do |_, args|
8-
exec("que ./config/environment.rb que/prometheus #{args.extras.join}")
10+
# Build base URL
11+
# Workaround for https://github.com/que-rb/que/issues/442
12+
db_config = ActiveRecord::Base.connection_db_config.configuration_hash
13+
connection_url = Que::DBConnectionURL.build_connection_url(db_config)
14+
15+
exec("que --connection-url '#{connection_url}' ./config/environment.rb que/prometheus #{args.extras.join}")
916
end
1017

1118
desc 'Reschedule all jobs to be executed now'
@@ -20,3 +27,4 @@ namespace :que do
2027
Model.find_each(&UpdateJob.method(:perform_later))
2128
end
2229
end
30+

0 commit comments

Comments
 (0)