kamal 2.3.0 → 2.7.0
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/lib/kamal/cli/accessory.rb +42 -16
- data/lib/kamal/cli/alias/command.rb +1 -0
- data/lib/kamal/cli/app/{prepare_assets.rb → assets.rb} +1 -1
- data/lib/kamal/cli/app/boot.rb +3 -2
- data/lib/kamal/cli/app/error_pages.rb +33 -0
- data/lib/kamal/cli/app/ssl_certificates.rb +28 -0
- data/lib/kamal/cli/app.rb +94 -29
- data/lib/kamal/cli/base.rb +29 -4
- data/lib/kamal/cli/build.rb +60 -18
- data/lib/kamal/cli/main.rb +8 -10
- data/lib/kamal/cli/proxy.rb +58 -25
- data/lib/kamal/cli/registry.rb +2 -0
- data/lib/kamal/cli/secrets.rb +9 -3
- data/lib/kamal/cli/server.rb +4 -2
- data/lib/kamal/cli/templates/deploy.yml +6 -3
- data/lib/kamal/cli/templates/sample_hooks/post-app-boot.sample +3 -0
- data/lib/kamal/cli/templates/sample_hooks/post-deploy.sample +1 -1
- data/lib/kamal/cli/templates/sample_hooks/pre-app-boot.sample +3 -0
- data/lib/kamal/cli/templates/sample_hooks/pre-build.sample +1 -1
- data/lib/kamal/cli/templates/sample_hooks/pre-connect.sample +1 -1
- data/lib/kamal/cli/templates/sample_hooks/pre-deploy.sample +19 -6
- data/lib/kamal/cli.rb +1 -0
- data/lib/kamal/commander/specifics.rb +9 -1
- data/lib/kamal/commander.rb +18 -27
- data/lib/kamal/commands/accessory/proxy.rb +16 -0
- data/lib/kamal/commands/accessory.rb +9 -9
- data/lib/kamal/commands/app/assets.rb +4 -4
- data/lib/kamal/commands/app/containers.rb +2 -2
- data/lib/kamal/commands/app/error_pages.rb +9 -0
- data/lib/kamal/commands/app/execution.rb +6 -4
- data/lib/kamal/commands/app/images.rb +1 -1
- data/lib/kamal/commands/app/logging.rb +14 -4
- data/lib/kamal/commands/app/proxy.rb +17 -1
- data/lib/kamal/commands/app.rb +19 -10
- data/lib/kamal/commands/auditor.rb +11 -5
- data/lib/kamal/commands/base.rb +37 -1
- data/lib/kamal/commands/builder/base.rb +20 -7
- data/lib/kamal/commands/builder/cloud.rb +22 -0
- data/lib/kamal/commands/builder/pack.rb +46 -0
- data/lib/kamal/commands/builder.rb +11 -19
- data/lib/kamal/commands/proxy.rb +55 -15
- data/lib/kamal/commands/registry.rb +9 -7
- data/lib/kamal/configuration/accessory.rb +66 -11
- data/lib/kamal/configuration/builder.rb +20 -0
- data/lib/kamal/configuration/docs/accessory.yml +32 -4
- data/lib/kamal/configuration/docs/alias.yml +2 -2
- data/lib/kamal/configuration/docs/builder.yml +22 -0
- data/lib/kamal/configuration/docs/configuration.yml +6 -0
- data/lib/kamal/configuration/docs/env.yml +31 -0
- data/lib/kamal/configuration/docs/proxy.yml +78 -15
- data/lib/kamal/configuration/docs/registry.yml +4 -0
- data/lib/kamal/configuration/env.rb +13 -4
- data/lib/kamal/configuration/proxy/boot.rb +129 -0
- data/lib/kamal/configuration/proxy.rb +67 -5
- data/lib/kamal/configuration/registry.rb +6 -6
- data/lib/kamal/configuration/role.rb +11 -9
- data/lib/kamal/configuration/servers.rb +8 -1
- data/lib/kamal/configuration/validator/accessory.rb +6 -2
- data/lib/kamal/configuration/validator/builder.rb +2 -0
- data/lib/kamal/configuration/validator/proxy.rb +10 -0
- data/lib/kamal/configuration/validator/role.rb +3 -1
- data/lib/kamal/configuration/validator/servers.rb +1 -1
- data/lib/kamal/configuration/validator.rb +21 -1
- data/lib/kamal/configuration.rb +36 -57
- data/lib/kamal/docker.rb +30 -0
- data/lib/kamal/git.rb +10 -0
- data/lib/kamal/secrets/adapters/aws_secrets_manager.rb +51 -0
- data/lib/kamal/secrets/adapters/base.rb +13 -3
- data/lib/kamal/secrets/adapters/bitwarden.rb +2 -2
- data/lib/kamal/secrets/adapters/bitwarden_secrets_manager.rb +66 -0
- data/lib/kamal/secrets/adapters/doppler.rb +57 -0
- data/lib/kamal/secrets/adapters/enpass.rb +71 -0
- data/lib/kamal/secrets/adapters/gcp_secret_manager.rb +112 -0
- data/lib/kamal/secrets/adapters/last_pass.rb +3 -2
- data/lib/kamal/secrets/adapters/one_password.rb +47 -13
- data/lib/kamal/secrets/adapters/passbolt.rb +130 -0
- data/lib/kamal/secrets/adapters/test.rb +2 -2
- data/lib/kamal/secrets/adapters.rb +2 -0
- data/lib/kamal/secrets/dotenv/inline_command_substitution.rb +2 -1
- data/lib/kamal/secrets.rb +1 -1
- data/lib/kamal/version.rb +1 -1
- metadata +22 -10
@@ -1,14 +1,16 @@
|
|
1
1
|
class Kamal::Commands::Registry < Kamal::Commands::Base
|
2
|
-
|
2
|
+
def login(registry_config: nil)
|
3
|
+
registry_config ||= config.registry
|
3
4
|
|
4
|
-
def login
|
5
5
|
docker :login,
|
6
|
-
|
7
|
-
"-u", sensitive(Kamal::Utils.escape_shell_value(
|
8
|
-
"-p", sensitive(Kamal::Utils.escape_shell_value(
|
6
|
+
registry_config.server,
|
7
|
+
"-u", sensitive(Kamal::Utils.escape_shell_value(registry_config.username)),
|
8
|
+
"-p", sensitive(Kamal::Utils.escape_shell_value(registry_config.password))
|
9
9
|
end
|
10
10
|
|
11
|
-
def logout
|
12
|
-
|
11
|
+
def logout(registry_config: nil)
|
12
|
+
registry_config ||= config.registry
|
13
|
+
|
14
|
+
docker :logout, registry_config.server
|
13
15
|
end
|
14
16
|
end
|
@@ -5,7 +5,7 @@ class Kamal::Configuration::Accessory
|
|
5
5
|
|
6
6
|
delegate :argumentize, :optionize, to: Kamal::Utils
|
7
7
|
|
8
|
-
attr_reader :name, :
|
8
|
+
attr_reader :name, :env, :proxy, :registry
|
9
9
|
|
10
10
|
def initialize(name, config:)
|
11
11
|
@name, @config, @accessory_config = name.inquiry, config, config.raw_config["accessories"][name]
|
@@ -16,10 +16,11 @@ class Kamal::Configuration::Accessory
|
|
16
16
|
context: "accessories/#{name}",
|
17
17
|
with: Kamal::Configuration::Validator::Accessory
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
ensure_valid_roles
|
20
|
+
|
21
|
+
@env = initialize_env
|
22
|
+
@proxy = initialize_proxy if running_proxy?
|
23
|
+
@registry = initialize_registry if accessory_config["registry"].present?
|
23
24
|
end
|
24
25
|
|
25
26
|
def service_name
|
@@ -27,11 +28,11 @@ class Kamal::Configuration::Accessory
|
|
27
28
|
end
|
28
29
|
|
29
30
|
def image
|
30
|
-
accessory_config["image"]
|
31
|
+
[ registry&.server, accessory_config["image"] ].compact.join("/")
|
31
32
|
end
|
32
33
|
|
33
34
|
def hosts
|
34
|
-
hosts_from_host || hosts_from_hosts || hosts_from_roles
|
35
|
+
hosts_from_host || hosts_from_hosts || hosts_from_roles || hosts_from_tags
|
35
36
|
end
|
36
37
|
|
37
38
|
def port
|
@@ -106,8 +107,34 @@ class Kamal::Configuration::Accessory
|
|
106
107
|
accessory_config["cmd"]
|
107
108
|
end
|
108
109
|
|
110
|
+
def running_proxy?
|
111
|
+
accessory_config["proxy"].present?
|
112
|
+
end
|
113
|
+
|
109
114
|
private
|
110
|
-
|
115
|
+
attr_reader :config, :accessory_config
|
116
|
+
|
117
|
+
def initialize_env
|
118
|
+
Kamal::Configuration::Env.new \
|
119
|
+
config: accessory_config.fetch("env", {}),
|
120
|
+
secrets: config.secrets,
|
121
|
+
context: "accessories/#{name}/env"
|
122
|
+
end
|
123
|
+
|
124
|
+
def initialize_proxy
|
125
|
+
Kamal::Configuration::Proxy.new \
|
126
|
+
config: config,
|
127
|
+
proxy_config: accessory_config["proxy"],
|
128
|
+
context: "accessories/#{name}/proxy",
|
129
|
+
secrets: config.secrets
|
130
|
+
end
|
131
|
+
|
132
|
+
def initialize_registry
|
133
|
+
Kamal::Configuration::Registry.new \
|
134
|
+
config: accessory_config,
|
135
|
+
secrets: config.secrets,
|
136
|
+
context: "accessories/#{name}/registry"
|
137
|
+
end
|
111
138
|
|
112
139
|
def default_labels
|
113
140
|
{ "service" => service_name }
|
@@ -129,7 +156,7 @@ class Kamal::Configuration::Accessory
|
|
129
156
|
end
|
130
157
|
|
131
158
|
def read_dynamic_file(local_file)
|
132
|
-
StringIO.new(ERB.new(
|
159
|
+
StringIO.new(ERB.new(File.read(local_file)).result)
|
133
160
|
end
|
134
161
|
|
135
162
|
def expand_remote_file(remote_file)
|
@@ -175,12 +202,40 @@ class Kamal::Configuration::Accessory
|
|
175
202
|
end
|
176
203
|
|
177
204
|
def hosts_from_roles
|
178
|
-
if accessory_config.key?("
|
179
|
-
|
205
|
+
if accessory_config.key?("role")
|
206
|
+
config.role(accessory_config["role"])&.hosts
|
207
|
+
elsif accessory_config.key?("roles")
|
208
|
+
accessory_config["roles"].flat_map { |role| config.role(role)&.hosts }
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def hosts_from_tags
|
213
|
+
if accessory_config.key?("tag")
|
214
|
+
extract_hosts_from_config_with_tag(accessory_config["tag"])
|
215
|
+
elsif accessory_config.key?("tags")
|
216
|
+
accessory_config["tags"].flat_map { |tag| extract_hosts_from_config_with_tag(tag) }
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def extract_hosts_from_config_with_tag(tag)
|
221
|
+
if (servers_with_roles = config.raw_config.servers).is_a?(Hash)
|
222
|
+
servers_with_roles.flat_map do |role, servers_in_role|
|
223
|
+
servers_in_role.filter_map do |host|
|
224
|
+
host.keys.first if host.is_a?(Hash) && host.values.first.include?(tag)
|
225
|
+
end
|
226
|
+
end
|
180
227
|
end
|
181
228
|
end
|
182
229
|
|
183
230
|
def network
|
184
231
|
accessory_config["network"] || DEFAULT_NETWORK
|
185
232
|
end
|
233
|
+
|
234
|
+
def ensure_valid_roles
|
235
|
+
if accessory_config["roles"] && (missing_roles = accessory_config["roles"] - config.roles.map(&:name)).any?
|
236
|
+
raise Kamal::ConfigurationError, "accessories/#{name}: unknown roles #{missing_roles.join(", ")}"
|
237
|
+
elsif accessory_config["role"] && !config.role(accessory_config["role"])
|
238
|
+
raise Kamal::ConfigurationError, "accessories/#{name}: unknown role #{accessory_config["role"]}"
|
239
|
+
end
|
240
|
+
end
|
186
241
|
end
|
@@ -53,10 +53,18 @@ class Kamal::Configuration::Builder
|
|
53
53
|
!local_disabled? && (arches.empty? || local_arches.any?)
|
54
54
|
end
|
55
55
|
|
56
|
+
def cloud?
|
57
|
+
driver.start_with? "cloud"
|
58
|
+
end
|
59
|
+
|
56
60
|
def cached?
|
57
61
|
!!builder_config["cache"]
|
58
62
|
end
|
59
63
|
|
64
|
+
def pack?
|
65
|
+
!!builder_config["pack"]
|
66
|
+
end
|
67
|
+
|
60
68
|
def args
|
61
69
|
builder_config["args"] || {}
|
62
70
|
end
|
@@ -81,6 +89,14 @@ class Kamal::Configuration::Builder
|
|
81
89
|
builder_config.fetch("driver", "docker-container")
|
82
90
|
end
|
83
91
|
|
92
|
+
def pack_builder
|
93
|
+
builder_config["pack"]["builder"] if pack?
|
94
|
+
end
|
95
|
+
|
96
|
+
def pack_buildpacks
|
97
|
+
builder_config["pack"]["buildpacks"] if pack?
|
98
|
+
end
|
99
|
+
|
84
100
|
def local_disabled?
|
85
101
|
builder_config["local"] == false
|
86
102
|
end
|
@@ -115,6 +131,10 @@ class Kamal::Configuration::Builder
|
|
115
131
|
builder_config["provenance"]
|
116
132
|
end
|
117
133
|
|
134
|
+
def sbom
|
135
|
+
builder_config["sbom"]
|
136
|
+
end
|
137
|
+
|
118
138
|
def git_clone?
|
119
139
|
Kamal::Git.used? && builder_config["context"].nil?
|
120
140
|
end
|
@@ -23,18 +23,41 @@ accessories:
|
|
23
23
|
|
24
24
|
# Image
|
25
25
|
#
|
26
|
-
# The Docker image to use
|
26
|
+
# The Docker image to use.
|
27
|
+
# Prefix it with its server when using root level registry different from Docker Hub.
|
28
|
+
# Define registry directly or via anchors when it differs from root level registry.
|
27
29
|
image: mysql:8.0
|
28
30
|
|
31
|
+
# Registry
|
32
|
+
#
|
33
|
+
# By default accessories use Docker Hub registry.
|
34
|
+
# You can specify different registry per accessory with this option.
|
35
|
+
# Don't prefix image with this registry server.
|
36
|
+
# Use anchors if you need to set the same specific registry for several accessories.
|
37
|
+
#
|
38
|
+
# ```yml
|
39
|
+
# registry:
|
40
|
+
# <<: *specific-registry
|
41
|
+
# ```
|
42
|
+
#
|
43
|
+
# See kamal docs registry for more information:
|
44
|
+
registry:
|
45
|
+
...
|
46
|
+
|
29
47
|
# Accessory hosts
|
30
48
|
#
|
31
|
-
# Specify one of `host`, `hosts`, or `
|
49
|
+
# Specify one of `host`, `hosts`, `role`, `roles`, `tag` or `tags`:
|
32
50
|
host: mysql-db1
|
33
51
|
hosts:
|
34
52
|
- mysql-db1
|
35
53
|
- mysql-db2
|
54
|
+
role: mysql
|
36
55
|
roles:
|
37
56
|
- mysql
|
57
|
+
tag: writer
|
58
|
+
tags:
|
59
|
+
- writer
|
60
|
+
- reader
|
38
61
|
|
39
62
|
# Custom command
|
40
63
|
#
|
@@ -43,8 +66,8 @@ accessories:
|
|
43
66
|
|
44
67
|
# Port mappings
|
45
68
|
#
|
46
|
-
# See https://docs.docker.com/network
|
47
|
-
# implications of exposing ports publicly.
|
69
|
+
# See [https://docs.docker.com/network/](https://docs.docker.com/network/), and
|
70
|
+
# especially note the warning about the security implications of exposing ports publicly.
|
48
71
|
port: "127.0.0.1:3306:3306"
|
49
72
|
|
50
73
|
# Labels
|
@@ -98,3 +121,8 @@ accessories:
|
|
98
121
|
# Defaults to kamal:
|
99
122
|
network: custom
|
100
123
|
|
124
|
+
# Proxy
|
125
|
+
#
|
126
|
+
# You can run your accessory behind the Kamal proxy. See kamal docs proxy for more information
|
127
|
+
proxy:
|
128
|
+
...
|
@@ -5,12 +5,12 @@
|
|
5
5
|
# For example, for a Rails app, you might open a console with:
|
6
6
|
#
|
7
7
|
# ```shell
|
8
|
-
# kamal app exec -i
|
8
|
+
# kamal app exec -i --reuse "bin/rails console"
|
9
9
|
# ```
|
10
10
|
#
|
11
11
|
# By defining an alias, like this:
|
12
12
|
aliases:
|
13
|
-
console: app exec -
|
13
|
+
console: app exec -i --reuse "bin/rails console"
|
14
14
|
# You can now open the console with:
|
15
15
|
#
|
16
16
|
# ```shell
|
@@ -31,6 +31,19 @@ builder:
|
|
31
31
|
# Defaults to true:
|
32
32
|
local: true
|
33
33
|
|
34
|
+
# Buildpack configuration
|
35
|
+
#
|
36
|
+
# The build configuration for using pack to build a Cloud Native Buildpack image.
|
37
|
+
#
|
38
|
+
# For additional buildpack customization options you can create a project descriptor
|
39
|
+
# file(project.toml) that the Pack CLI will automatically use.
|
40
|
+
# See https://buildpacks.io/docs/for-app-developers/how-to/build-inputs/use-project-toml/ for more information.
|
41
|
+
pack:
|
42
|
+
builder: heroku/builder:24
|
43
|
+
buildpacks:
|
44
|
+
- heroku/ruby
|
45
|
+
- heroku/procfile
|
46
|
+
|
34
47
|
# Builder cache
|
35
48
|
#
|
36
49
|
# The type must be either 'gha' or 'registry'.
|
@@ -102,9 +115,18 @@ builder:
|
|
102
115
|
#
|
103
116
|
# The build driver to use, defaults to `docker-container`:
|
104
117
|
driver: docker
|
118
|
+
#
|
119
|
+
# If you want to use Docker Build Cloud (https://www.docker.com/products/build-cloud/), you can set the driver to:
|
120
|
+
driver: cloud org-name/builder-name
|
105
121
|
|
106
122
|
# Provenance
|
107
123
|
#
|
108
124
|
# It is used to configure provenance attestations for the build result.
|
109
125
|
# The value can also be a boolean to enable or disable provenance attestations.
|
110
126
|
provenance: mode=max
|
127
|
+
|
128
|
+
# SBOM (Software Bill of Materials)
|
129
|
+
#
|
130
|
+
# It is used to configure SBOM generation for the build result.
|
131
|
+
# The value can also be a boolean to enable or disable SBOM generation.
|
132
|
+
sbom: true
|
@@ -82,6 +82,12 @@ asset_path: /path/to/assets
|
|
82
82
|
# See https://kamal-deploy.org/docs/hooks for more information:
|
83
83
|
hooks_path: /user_home/kamal/hooks
|
84
84
|
|
85
|
+
# Error pages
|
86
|
+
#
|
87
|
+
# A directory relative to the app root to find error pages for the proxy to serve.
|
88
|
+
# Any files in the format 4xx.html or 5xx.html will be copied to the hosts.
|
89
|
+
error_pages_path: public
|
90
|
+
|
85
91
|
# Require destinations
|
86
92
|
#
|
87
93
|
# Whether deployments require a destination to be specified, defaults to `false`:
|
@@ -51,6 +51,37 @@ env:
|
|
51
51
|
secret:
|
52
52
|
- DB_PASSWORD
|
53
53
|
|
54
|
+
# Aliased secrets
|
55
|
+
#
|
56
|
+
# You can also alias secrets to other secrets using a `:` separator.
|
57
|
+
#
|
58
|
+
# This is useful when the ENV name is different from the secret name. For example, if you have two
|
59
|
+
# places where you need to define the ENV variable `DB_PASSWORD`, but the value is different depending
|
60
|
+
# on the context.
|
61
|
+
#
|
62
|
+
# ```shell
|
63
|
+
# SECRETS=$(kamal secrets fetch ...)
|
64
|
+
#
|
65
|
+
# MAIN_DB_PASSWORD=$(kamal secrets extract MAIN_DB_PASSWORD $SECRETS)
|
66
|
+
# SECONDARY_DB_PASSWORD=$(kamal secrets extract SECONDARY_DB_PASSWORD $SECRETS)
|
67
|
+
# ```
|
68
|
+
env:
|
69
|
+
secret:
|
70
|
+
- DB_PASSWORD:MAIN_DB_PASSWORD
|
71
|
+
tags:
|
72
|
+
secondary_db:
|
73
|
+
secret:
|
74
|
+
- DB_PASSWORD:SECONDARY_DB_PASSWORD
|
75
|
+
accessories:
|
76
|
+
main_db_accessory:
|
77
|
+
env:
|
78
|
+
secret:
|
79
|
+
- DB_PASSWORD:MAIN_DB_PASSWORD
|
80
|
+
secondary_db_accessory:
|
81
|
+
env:
|
82
|
+
secret:
|
83
|
+
- DB_PASSWORD:SECONDARY_DB_PASSWORD
|
84
|
+
|
54
85
|
# Tags
|
55
86
|
#
|
56
87
|
# Tags are used to add extra env variables to specific hosts.
|
@@ -10,11 +10,6 @@
|
|
10
10
|
# They are application-specific, so they are not shared when multiple applications
|
11
11
|
# run on the same proxy.
|
12
12
|
#
|
13
|
-
# The proxy is enabled by default on the primary role but can be disabled by
|
14
|
-
# setting `proxy: false`.
|
15
|
-
#
|
16
|
-
# It is disabled by default on all other roles but can be enabled by setting
|
17
|
-
# `proxy: true` or providing a proxy configuration.
|
18
13
|
proxy:
|
19
14
|
|
20
15
|
# Hosts
|
@@ -46,14 +41,65 @@ proxy:
|
|
46
41
|
# The host value must point to the server we are deploying to, and port 443 must be
|
47
42
|
# open for the Let's Encrypt challenge to succeed.
|
48
43
|
#
|
44
|
+
# If you set `ssl` to `true`, `kamal-proxy` will stop forwarding headers to your app,
|
45
|
+
# unless you explicitly set `forward_headers: true`
|
46
|
+
#
|
49
47
|
# Defaults to `false`:
|
50
|
-
ssl:
|
48
|
+
ssl: ...
|
49
|
+
|
50
|
+
# Custom SSL certificate
|
51
|
+
#
|
52
|
+
# In some cases, using Let's Encrypt for automatic certificate management is not an
|
53
|
+
# option, for example if you are running from host than one host. Or you may already
|
54
|
+
# have SSL certificates issued by a different Certificate Authority (CA).
|
55
|
+
# Kamal supports loading custom SSL certificates
|
56
|
+
# directly from secrets.
|
57
|
+
#
|
58
|
+
# Examples:
|
59
|
+
# ssl: true # Enable SSL with Let's Encrypt
|
60
|
+
# ssl: false # Disable SSL
|
61
|
+
# ssl: # Enable custom SSL
|
62
|
+
# certificate_pem: CERTIFICATE_PEM
|
63
|
+
# private_key_pem: PRIVATE_KEY_PEM
|
64
|
+
#
|
65
|
+
# ### Notes
|
66
|
+
# - If the certificate or key is missing or invalid, kamal-proxy will fail to start.
|
67
|
+
# - Always handle SSL certificates and private keys securely. Avoid hard-coding them in deploy.yml files or source control.
|
68
|
+
# - For automated certificate management, consider using the built-in Let's Encrypt integration instead.
|
69
|
+
|
70
|
+
# SSL redirect
|
71
|
+
#
|
72
|
+
# By default, kamal-proxy will redirect all HTTP requests to HTTPS when SSL is enabled.
|
73
|
+
# If you prefer that HTTP traffic is passed through to your application (along with
|
74
|
+
# HTTPS traffic), you can disable this redirect by setting `ssl_redirect: false`:
|
75
|
+
ssl_redirect: false
|
76
|
+
|
77
|
+
# Forward headers
|
78
|
+
#
|
79
|
+
# Whether to forward the `X-Forwarded-For` and `X-Forwarded-Proto` headers.
|
80
|
+
#
|
81
|
+
# If you are behind a trusted proxy, you can set this to `true` to forward the headers.
|
82
|
+
#
|
83
|
+
# By default, kamal-proxy will not forward the headers if the `ssl` option is set to `true`, and
|
84
|
+
# will forward them if it is set to `false`.
|
85
|
+
forward_headers: true
|
51
86
|
|
52
87
|
# Response timeout
|
53
88
|
#
|
54
89
|
# How long to wait for requests to complete before timing out, defaults to 30 seconds:
|
55
90
|
response_timeout: 10
|
56
91
|
|
92
|
+
# Path-based routing
|
93
|
+
#
|
94
|
+
# For applications that split their traffic to different services based on the request path,
|
95
|
+
# you can use path-based routing to mount services under different path prefixes.
|
96
|
+
path_prefix: '/api'
|
97
|
+
# By default, the path prefix will be stripped from the request before it is forwarded upstream.
|
98
|
+
# So in the example above, a request to /api/users/123 will be forwarded to web-1 as /users/123.
|
99
|
+
# To instead forward the request with the original path (including the prefix),
|
100
|
+
# specify --strip-path-prefix=false
|
101
|
+
strip_path_prefix: false
|
102
|
+
|
57
103
|
# Healthcheck
|
58
104
|
#
|
59
105
|
# When deploying, the proxy will by default hit `/up` once every second until we hit
|
@@ -94,12 +140,29 @@ proxy:
|
|
94
140
|
- X-Request-ID
|
95
141
|
- X-Request-Start
|
96
142
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
143
|
+
# Enabling/disabling the proxy on roles
|
144
|
+
#
|
145
|
+
# The proxy is enabled by default on the primary role but can be disabled by
|
146
|
+
# setting `proxy: false` in the primary role's configuration.
|
147
|
+
#
|
148
|
+
# ```yaml
|
149
|
+
# servers:
|
150
|
+
# web:
|
151
|
+
# hosts:
|
152
|
+
# - ...
|
153
|
+
# proxy: false
|
154
|
+
# ```
|
155
|
+
#
|
156
|
+
# It is disabled by default on all other roles but can be enabled by setting
|
157
|
+
# `proxy: true` or providing a proxy configuration for that role.
|
158
|
+
#
|
159
|
+
# ```yaml
|
160
|
+
# servers:
|
161
|
+
# web:
|
162
|
+
# hosts:
|
163
|
+
# - ...
|
164
|
+
# web2:
|
165
|
+
# hosts:
|
166
|
+
# - ...
|
167
|
+
# proxy: true
|
168
|
+
# ```
|
@@ -2,6 +2,10 @@
|
|
2
2
|
#
|
3
3
|
# The default registry is Docker Hub, but you can change it using `registry/server`.
|
4
4
|
#
|
5
|
+
# By default, Docker Hub creates public repositories. To avoid making your images public,
|
6
|
+
# set up a private repository before deploying, or change the default repository privacy
|
7
|
+
# settings to private in your [Docker Hub settings](https://hub.docker.com/repository-settings/default-privacy).
|
8
|
+
#
|
5
9
|
# A reference to a secret (in this case, `DOCKER_REGISTRY_TOKEN`) will look up the secret
|
6
10
|
# in the local environment:
|
7
11
|
registry:
|
@@ -1,8 +1,7 @@
|
|
1
1
|
class Kamal::Configuration::Env
|
2
2
|
include Kamal::Configuration::Validation
|
3
3
|
|
4
|
-
attr_reader :context, :
|
5
|
-
attr_reader :clear, :secret_keys
|
4
|
+
attr_reader :context, :clear, :secret_keys
|
6
5
|
delegate :argumentize, to: Kamal::Utils
|
7
6
|
|
8
7
|
def initialize(config:, secrets:, context: "env")
|
@@ -18,12 +17,22 @@ class Kamal::Configuration::Env
|
|
18
17
|
end
|
19
18
|
|
20
19
|
def secrets_io
|
21
|
-
Kamal::EnvFile.new(
|
20
|
+
Kamal::EnvFile.new(aliased_secrets).to_io
|
22
21
|
end
|
23
22
|
|
24
23
|
def merge(other)
|
25
24
|
self.class.new \
|
26
25
|
config: { "clear" => clear.merge(other.clear), "secret" => secret_keys | other.secret_keys },
|
27
|
-
secrets: secrets
|
26
|
+
secrets: @secrets
|
28
27
|
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def aliased_secrets
|
31
|
+
secret_keys.to_h { |key| extract_alias(key) }.transform_values { |secret_key| @secrets[secret_key] }
|
32
|
+
end
|
33
|
+
|
34
|
+
def extract_alias(key)
|
35
|
+
key_name, key_aliased_to = key.split(":", 2)
|
36
|
+
[ key_name, key_aliased_to || key_name ]
|
37
|
+
end
|
29
38
|
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
class Kamal::Configuration::Proxy::Boot
|
2
|
+
MINIMUM_VERSION = "v0.9.0"
|
3
|
+
DEFAULT_HTTP_PORT = 80
|
4
|
+
DEFAULT_HTTPS_PORT = 443
|
5
|
+
DEFAULT_LOG_MAX_SIZE = "10m"
|
6
|
+
|
7
|
+
attr_reader :config
|
8
|
+
delegate :argumentize, :optionize, to: Kamal::Utils
|
9
|
+
|
10
|
+
def initialize(config:)
|
11
|
+
@config = config
|
12
|
+
end
|
13
|
+
|
14
|
+
def publish_args(http_port, https_port, bind_ips = nil)
|
15
|
+
ensure_valid_bind_ips(bind_ips)
|
16
|
+
|
17
|
+
(bind_ips || [ nil ]).map do |bind_ip|
|
18
|
+
bind_ip = format_bind_ip(bind_ip)
|
19
|
+
publish_http = [ bind_ip, http_port, DEFAULT_HTTP_PORT ].compact.join(":")
|
20
|
+
publish_https = [ bind_ip, https_port, DEFAULT_HTTPS_PORT ].compact.join(":")
|
21
|
+
|
22
|
+
argumentize "--publish", [ publish_http, publish_https ]
|
23
|
+
end.join(" ")
|
24
|
+
end
|
25
|
+
|
26
|
+
def logging_args(max_size)
|
27
|
+
argumentize "--log-opt", "max-size=#{max_size}" if max_size.present?
|
28
|
+
end
|
29
|
+
|
30
|
+
def default_boot_options
|
31
|
+
[
|
32
|
+
*(publish_args(DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT, nil)),
|
33
|
+
*(logging_args(DEFAULT_LOG_MAX_SIZE))
|
34
|
+
]
|
35
|
+
end
|
36
|
+
|
37
|
+
def repository_name
|
38
|
+
"basecamp"
|
39
|
+
end
|
40
|
+
|
41
|
+
def image_name
|
42
|
+
"kamal-proxy"
|
43
|
+
end
|
44
|
+
|
45
|
+
def image_default
|
46
|
+
"#{repository_name}/#{image_name}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def container_name
|
50
|
+
"kamal-proxy"
|
51
|
+
end
|
52
|
+
|
53
|
+
def host_directory
|
54
|
+
File.join config.run_directory, "proxy"
|
55
|
+
end
|
56
|
+
|
57
|
+
def options_file
|
58
|
+
File.join host_directory, "options"
|
59
|
+
end
|
60
|
+
|
61
|
+
def image_file
|
62
|
+
File.join host_directory, "image"
|
63
|
+
end
|
64
|
+
|
65
|
+
def image_version_file
|
66
|
+
File.join host_directory, "image_version"
|
67
|
+
end
|
68
|
+
|
69
|
+
def run_command_file
|
70
|
+
File.join host_directory, "run_command"
|
71
|
+
end
|
72
|
+
|
73
|
+
def apps_directory
|
74
|
+
File.join host_directory, "apps-config"
|
75
|
+
end
|
76
|
+
|
77
|
+
def apps_container_directory
|
78
|
+
"/home/kamal-proxy/.apps-config"
|
79
|
+
end
|
80
|
+
|
81
|
+
def apps_volume
|
82
|
+
Kamal::Configuration::Volume.new \
|
83
|
+
host_path: apps_directory,
|
84
|
+
container_path: apps_container_directory
|
85
|
+
end
|
86
|
+
|
87
|
+
def app_directory
|
88
|
+
File.join apps_directory, config.service_and_destination
|
89
|
+
end
|
90
|
+
|
91
|
+
def app_container_directory
|
92
|
+
File.join apps_container_directory, config.service_and_destination
|
93
|
+
end
|
94
|
+
|
95
|
+
def error_pages_directory
|
96
|
+
File.join app_directory, "error_pages"
|
97
|
+
end
|
98
|
+
|
99
|
+
def error_pages_container_directory
|
100
|
+
File.join app_container_directory, "error_pages"
|
101
|
+
end
|
102
|
+
|
103
|
+
def tls_directory
|
104
|
+
File.join app_directory, "tls"
|
105
|
+
end
|
106
|
+
|
107
|
+
def tls_container_directory
|
108
|
+
File.join app_container_directory, "tls"
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
def ensure_valid_bind_ips(bind_ips)
|
113
|
+
bind_ips.present? && bind_ips.each do |ip|
|
114
|
+
next if ip =~ Resolv::IPv4::Regex || ip =~ Resolv::IPv6::Regex
|
115
|
+
raise ArgumentError, "Invalid publish IP address: #{ip}"
|
116
|
+
end
|
117
|
+
|
118
|
+
true
|
119
|
+
end
|
120
|
+
|
121
|
+
def format_bind_ip(ip)
|
122
|
+
# Ensure IPv6 address inside square brackets - e.g. [::1]
|
123
|
+
if ip =~ Resolv::IPv6::Regex && ip !~ /\A\[.*\]\z/
|
124
|
+
"[#{ip}]"
|
125
|
+
else
|
126
|
+
ip
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|