rb-portless 0.4.0 → 0.4.1.dev.20260707.17213cc
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/lib/portless/ca_bundle.rb +79 -0
- data/lib/portless/run_support.rb +4 -2
- data/lib/portless/version.rb +1 -1
- data/lib/rb-portless.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9c5231d955992c5d9c1dc235bce13b8ee6c5d50852147e50c8da632f1918c8a4
|
|
4
|
+
data.tar.gz: 60856b5d93cc1016c5a1fdde83ec692a681f1c060518c0437ffd9d642df190cf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3b391f24ff703e076ad3fb1df430024773cfbf1fb6acc9309ed8eb68d9395041db9e48521e5afd3a029fad80075ed6754c1f86a3ad7ab7fa033c5665a1be2443
|
|
7
|
+
data.tar.gz: 327c09c0c578019c00e0fc9f92513ad335f7faaf8408075d34003d2db2b37ba93571097149a009397ec89cf04de4d3b86a5ec03a6bb0ac43606010fe091f742a
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@ All notable changes to this project are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/), and the project adheres to
|
|
5
5
|
[Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [0.4.1]
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Proxied apps could no longer verify public TLS certificates.** `run` set the
|
|
12
|
+
child's `SSL_CERT_FILE` to our local CA so the app would trust sibling
|
|
13
|
+
`.localhost` hosts — but that env var *replaces* OpenSSL's trust store rather
|
|
14
|
+
than extending it, so the app lost every public root and all outbound HTTPS
|
|
15
|
+
(payment APIs, S3, webhooks, exchange-rate feeds…) failed with `certificate
|
|
16
|
+
verify failed (unable to get local issuer certificate)`. `run` now hands apps
|
|
17
|
+
a combined bundle (the system's default roots **plus** our CA), assembled once
|
|
18
|
+
into `~/.rb-portless/ca-bundle.pem` and rebuilt when either input changes, so
|
|
19
|
+
apps trust the public web *and* our local hosts. Falls back to leaving
|
|
20
|
+
`SSL_CERT_FILE` unset when the CA or system roots can't be located.
|
|
21
|
+
|
|
7
22
|
## [0.4.0]
|
|
8
23
|
|
|
9
24
|
### Added
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "openssl"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
|
|
6
|
+
module Portless
|
|
7
|
+
# The CA bundle handed to proxied apps via SSL_CERT_FILE.
|
|
8
|
+
#
|
|
9
|
+
# SSL_CERT_FILE *replaces* OpenSSL's trust file rather than adding to it, so
|
|
10
|
+
# pointing an app at our local CA alone makes it distrust every public
|
|
11
|
+
# certificate — Frankfurter, Stripe, S3, GitHub — and all outbound HTTPS from
|
|
12
|
+
# the dev server fails with "unable to get local issuer certificate". We
|
|
13
|
+
# instead assemble a combined bundle (the system's default roots + our CA) and
|
|
14
|
+
# point apps at that, so they trust the public web *and* sibling .localhost
|
|
15
|
+
# hosts served under our CA.
|
|
16
|
+
module CaBundle
|
|
17
|
+
# Well-known system trust stores, tried when OpenSSL's compiled-in default
|
|
18
|
+
# isn't readable (varies by distro / OpenSSL build).
|
|
19
|
+
FALLBACK_ROOTS = [
|
|
20
|
+
"/etc/ssl/cert.pem", # macOS, OpenBSD
|
|
21
|
+
"/etc/ssl/certs/ca-certificates.crt", # Debian, Ubuntu, Alpine
|
|
22
|
+
"/etc/pki/tls/certs/ca-bundle.crt", # Fedora, RHEL
|
|
23
|
+
"/etc/ssl/ca-bundle.pem" # openSUSE
|
|
24
|
+
].freeze
|
|
25
|
+
|
|
26
|
+
module_function
|
|
27
|
+
|
|
28
|
+
# Absolute path to the combined bundle, (re)building it when missing or older
|
|
29
|
+
# than either input. Returns nil when our CA doesn't exist yet or assembly
|
|
30
|
+
# fails, so the caller simply omits the SSL_CERT_FILE override (apps keep
|
|
31
|
+
# their own defaults) rather than shipping a broken trust store.
|
|
32
|
+
def path
|
|
33
|
+
return nil unless File.exist?(State.ca_cert)
|
|
34
|
+
|
|
35
|
+
bundle = bundle_path
|
|
36
|
+
rebuild(bundle) if stale?(bundle)
|
|
37
|
+
File.exist?(bundle) ? bundle : nil
|
|
38
|
+
rescue StandardError
|
|
39
|
+
nil
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def bundle_path = State.path("ca-bundle.pem")
|
|
43
|
+
|
|
44
|
+
def stale?(bundle)
|
|
45
|
+
return true unless File.exist?(bundle)
|
|
46
|
+
|
|
47
|
+
inputs = [ State.ca_cert, system_roots ].compact.select { |f| File.exist?(f) }
|
|
48
|
+
inputs.empty? || inputs.any? { |f| File.mtime(f) > File.mtime(bundle) }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def rebuild(bundle)
|
|
52
|
+
State.ensure_dir!
|
|
53
|
+
pems = []
|
|
54
|
+
roots = system_roots
|
|
55
|
+
pems << File.read(roots) if roots && File.exist?(roots)
|
|
56
|
+
pems << File.read(State.ca_cert)
|
|
57
|
+
|
|
58
|
+
# Atomic write: concurrent app starts may both rebuild; a temp file + rename
|
|
59
|
+
# keeps any reader from seeing a half-written bundle.
|
|
60
|
+
tmp = "#{bundle}.#{Process.pid}.tmp"
|
|
61
|
+
File.write(tmp, "#{pems.join("\n").strip}\n")
|
|
62
|
+
File.rename(tmp, bundle)
|
|
63
|
+
State.fix_ownership(bundle)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# The public root store OpenSSL would use by default, so our bundle is a
|
|
67
|
+
# superset of it. An operator-set SSL_CERT_FILE wins (but never our own
|
|
68
|
+
# bundle, which would read itself in circles).
|
|
69
|
+
def system_roots
|
|
70
|
+
env = ENV["SSL_CERT_FILE"].to_s
|
|
71
|
+
return env if !env.empty? && env != bundle_path && File.exist?(env)
|
|
72
|
+
|
|
73
|
+
default = OpenSSL::X509::DEFAULT_CERT_FILE
|
|
74
|
+
return default if default && File.exist?(default)
|
|
75
|
+
|
|
76
|
+
FALLBACK_ROOTS.find { |f| File.exist?(f) }
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
data/lib/portless/run_support.rb
CHANGED
|
@@ -12,8 +12,10 @@ module Portless
|
|
|
12
12
|
"PORT" => port.to_s,
|
|
13
13
|
"HOST" => "127.0.0.1",
|
|
14
14
|
"PORTLESS_URL" => url,
|
|
15
|
-
# Let the app's own server-side TLS verification trust our CA
|
|
16
|
-
|
|
15
|
+
# Let the app's own server-side TLS verification trust our CA — via a
|
|
16
|
+
# bundle that *also* carries the public roots, so SSL_CERT_FILE replacing
|
|
17
|
+
# the trust store doesn't break the app's outbound HTTPS. See CaBundle.
|
|
18
|
+
"SSL_CERT_FILE" => CaBundle.path
|
|
17
19
|
}.compact
|
|
18
20
|
# Our own bundle (rb-portless is loaded via the app's Bundler binstub) must
|
|
19
21
|
# not leak into the dev command — a foreman-style `bin/dev` isn't in the
|
data/lib/portless/version.rb
CHANGED
data/lib/rb-portless.rb
CHANGED
|
@@ -15,6 +15,7 @@ require_relative "portless/health"
|
|
|
15
15
|
require_relative "portless/privilege"
|
|
16
16
|
require_relative "portless/hosts"
|
|
17
17
|
require_relative "portless/certs"
|
|
18
|
+
require_relative "portless/ca_bundle"
|
|
18
19
|
require_relative "portless/trust"
|
|
19
20
|
require_relative "portless/proxy"
|
|
20
21
|
require_relative "portless/daemon"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rb-portless
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.1.dev.20260707.17213cc
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Afonso
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-07-
|
|
11
|
+
date: 2026-07-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: async
|
|
@@ -57,6 +57,7 @@ files:
|
|
|
57
57
|
- README.md
|
|
58
58
|
- exe/rb-portless
|
|
59
59
|
- lib/portless/banner.rb
|
|
60
|
+
- lib/portless/ca_bundle.rb
|
|
60
61
|
- lib/portless/certs.rb
|
|
61
62
|
- lib/portless/cli.rb
|
|
62
63
|
- lib/portless/colors.rb
|