smart_proxy_openbolt 1.1.0 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a6197325aea83c274a58cefe654d2fcf0f5afd29a6cd64b0ff66207f061b6a30
4
- data.tar.gz: e061867b08ce03aa03a235f48508ffe10d88abd3f4dbdd84d09ad1f6de90a6f3
3
+ metadata.gz: b6a0f4f6e883f4a2077ea3afccd8f244a1dfd27aafa73c6ce69f29ee423f921b
4
+ data.tar.gz: f74f56b1108bc2d0570af5135c225194d046e40e5741541265eff68c4bc61359
5
5
  SHA512:
6
- metadata.gz: 85fd5c0b3a05acd9f9ced2ad741bf688d70191db79204609a90f6e6d3cc5d1a14602d9e0719b32ee18c56e73e2775588a8167c5431c6ee17f72a7be78b57859f
7
- data.tar.gz: 9d79f77a3b4912d4e32056064f5a9fbf9d0e7efb06870d6693a21832dfc1f19287a741426a8494c897d4da0071749c28feca5af0dfafd4148ebd7ab88e82c374
6
+ metadata.gz: cfcc15bcfa344fa5a92b323ec6c9e9a11a0408ac49f111e1bf45e1eafdb228c5a557909d489cadde2594b54e8fd063de944dfff352bca9fca2950b4ac839477f
7
+ data.tar.gz: 427492ec2136bf63d811c50edd8fa24ab2642e0b54180b97bc02f4c396e9188fa8963d35d845bdc59f06bd0a591b7bdbf2e3317dd84d166ce2522b4a199e774d
data/README.md CHANGED
@@ -12,14 +12,14 @@ It exposes an HTTP API that the [foreman_openbolt](https://github.com/overlookin
12
12
  ## Introduction
13
13
 
14
14
  [OpenBolt](https://github.com/OpenVoxProject/openbolt) is the open source successor of [Bolt](https://github.com/puppetlabs/bolt) by [Perforce](https://www.perforce.com/).
15
- It runs Tasks and Plans against remote targets over SSH, WinRM, or other transports.
15
+ It runs Tasks and Plans against remote targets over SSH, WinRM, Choria, or other transports.
16
16
 
17
17
  This smart proxy plugin wraps the OpenBolt CLI and provides:
18
18
 
19
19
  * A REST API for listing available tasks, launching task runs, and retrieving results
20
20
  * Concurrent job execution via a configurable thread pool
21
21
  * Disk-based result storage so results survive proxy restarts
22
- * Transport option forwarding (SSH, WinRM) from the Foreman UI
22
+ * Transport option forwarding (SSH, WinRM, Choria) from the Foreman UI
23
23
 
24
24
  The Foreman UI talks to this plugin; it does not invoke OpenBolt directly. See the [foreman_openbolt README](https://github.com/overlookinfra/foreman_openbolt) for screenshots and the full user-facing workflow.
25
25
 
@@ -66,6 +66,72 @@ The plugin is configured in `settings.d/openbolt.yml` on the Smart Proxy host. A
66
66
  | `connect_timeout` | `30` | Connection timeout in seconds for target connections |
67
67
  | `log_dir` | `/var/log/foreman-proxy/openbolt` | Directory for job result files (created automatically if missing) |
68
68
 
69
+ After installing the plugin and restarting the smart proxy, refresh the
70
+ proxy features in Foreman so it detects the OpenBolt capability:
71
+
72
+ ```bash
73
+ foreman-rake openbolt:refresh_proxies
74
+ ```
75
+
76
+ ## Choria Transport
77
+
78
+ **Requires OpenBolt 5.5 or later.** The Choria transport is not available
79
+ in Puppet Bolt. SSH and WinRM transports work with any version.
80
+
81
+ The Choria transport works out of the box on HTTPS-enabled proxies. The
82
+ proxy ships a built-in MCollective client configuration and automatically
83
+ derives SSL paths and the MCollective certname from the proxy's own
84
+ OpenVox/Puppet certificates (configured via `ssl_certificate`,
85
+ `ssl_private_key`, and `ssl_ca_file` in `settings.yml`).
86
+
87
+ The `foreman-proxy` user must be in the `puppet` group to read the SSL
88
+ files. This is the default when the proxy is set up with
89
+ `foreman-installer` (the `puppet-foreman_proxy` module's
90
+ `manage_puppet_group` parameter handles this).
91
+
92
+ In common configurations where the Choria broker and Foreman are on the
93
+ same host, OpenVox/Puppet certificates are being used for authentication,
94
+ and Choria configuration is largely using default settings, the Choria
95
+ transport mostly just works out of the box with default settings. The one
96
+ setting you may need to evaluate is **Choria Brokers** (in the Foreman UI
97
+ under Administer > Settings > OpenBolt). If `puppet` resolves to the
98
+ broker host or SRV records are configured, it can be left blank. Otherwise,
99
+ set it to your broker's address (e.g. `primary.example.com:4222`). Other
100
+ Choria settings (SSL, a default config file we provide, certname) are
101
+ derived automatically and can be ignored unless your Choria configuration
102
+ requires customization.
103
+
104
+ ### Custom Choria configuration file
105
+
106
+ The built-in default config is at
107
+ `lib/smart_proxy_openbolt/config/choria-client.conf` in this gem. You
108
+ can view it to see what settings are included. To use a custom
109
+ MCollective client configuration file instead, set the "Choria Config
110
+ File" setting under Administer > Settings > OpenBolt. When a custom
111
+ config file is provided, the proxy does not inject SSL defaults (the
112
+ config file is expected to handle SSL on its own). SSL settings from
113
+ the Foreman UI still override the config file if set.
114
+
115
+ If your custom config file includes its own SSL paths, you should also
116
+ set **Choria MCollective Certname** to the CN of the certificate in your
117
+ config file. Without it, the MCollective client defaults to
118
+ `<user>.mcollective` (e.g. `foreman-proxy.mcollective`), which will fail
119
+ authentication if the certificate's CN doesn't match that pattern.
120
+
121
+ ### How it works
122
+
123
+ When the transport is Choria and no custom config file is provided:
124
+
125
+ 1. The proxy uses its built-in `choria-client.conf` (MCollective boilerplate)
126
+ 2. SSL cert, key, and CA default from the proxy's `settings.yml` SSL paths
127
+ 3. The MCollective certname is read from the certificate's CN
128
+ 4. All values are passed as OpenBolt CLI flags (`--choria-ssl-cert`,
129
+ `--choria-ssl-key`, `--choria-ssl-ca`, `--choria-mcollective-certname`,
130
+ `--choria-config-file`)
131
+
132
+ For full Choria infrastructure setup (broker, managed nodes, Puppet modules),
133
+ see the [Choria Testing](https://github.com/overlookinfra/foreman_openbolt/blob/main/docs/choria-testing.md) guide in the foreman_openbolt repo.
134
+
69
135
  ## API
70
136
 
71
137
  The plugin mounts its API at `/openbolt` on the Smart Proxy. All endpoints are used by the Foreman plugin and are not intended for direct use, but can be useful for debugging.
@@ -74,7 +140,7 @@ The plugin mounts its API at `/openbolt` on the Smart Proxy. All endpoints are u
74
140
  |--------|----------|-------------|
75
141
  | `GET` | `/openbolt/tasks` | List available tasks from the configured environment |
76
142
  | `GET` | `/openbolt/tasks/reload` | Clear the task cache and reload from disk |
77
- | `GET` | `/openbolt/tasks/options` | Get available transport options (SSH, WinRM) |
143
+ | `GET` | `/openbolt/tasks/options` | Get available transport options (SSH, WinRM, Choria) |
78
144
  | `POST` | `/openbolt/launch/task` | Launch a task run against specified targets |
79
145
  | `GET` | `/openbolt/job/:id/status` | Get the status of a running or completed job |
80
146
  | `GET` | `/openbolt/job/:id/result` | Get the full result of a completed job |
@@ -177,18 +243,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
177
243
 
178
244
  ### Release steps
179
245
 
180
- 1. Bump the version in `lib/smart_proxy_openbolt/version.rb`
181
- 2. Generate the changelog:
182
- ```bash
183
- CHANGELOG_GITHUB_TOKEN=github_pat_... bundle exec rake changelog
184
- ```
185
- 3. Create a PR with the version bump and changelog, get it reviewed and merged
186
- 4. Create and push a tag matching the version:
187
- ```bash
188
- git tag 0.2.0
189
- git push origin 0.2.0
190
- ```
191
- 5. The [release workflow](.github/workflows/release.yml) runs automatically on tag push and:
246
+ 1. Go to [Actions > Prepare Release](../../actions/workflows/prepare_release.yml) and run the workflow with the version to release (e.g. `1.2.0`)
247
+ 2. The workflow bumps the version in `version.rb`, generates the changelog, and opens a PR with the `skip-changelog` label
248
+ 3. Review and merge the PR
249
+ 4. Go to [Actions > Release](../../actions/workflows/release.yml) and run the workflow with the same version
250
+ 5. The release workflow:
251
+ - Verifies the version in `version.rb` matches the input
252
+ - Creates and pushes a git tag
192
253
  - Builds the gem
193
254
  - Creates a GitHub Release with auto-generated notes and the gem attached
194
255
  - Publishes the gem to GitHub Packages
@@ -0,0 +1,25 @@
1
+ collectives = mcollective
2
+ main_collective = mcollective
3
+ connector = nats
4
+ libdir = /opt/puppetlabs/mcollective/plugins
5
+ logger_type = console
6
+ loglevel = warn
7
+ securityprovider = choria
8
+
9
+ # This is the default Choria client configuration shipped with
10
+ # smart_proxy_openbolt. SSL paths, the MCollective certname, and
11
+ # Choria broker addresses are provided by the proxy via OpenBolt
12
+ # CLI flags and do not need to be configured here.
13
+ #
14
+ # To use a custom configuration file instead, set the "Choria Config
15
+ # File" option under Administer > Settings > OpenBolt. If your config
16
+ # file includes SSL
17
+ # paths, leave the Choria SSL settings in Foreman blank and they
18
+ # will be read from the config file.
19
+ #
20
+ # Example SSL configuration for a custom config file:
21
+ #
22
+ # plugin.security.provider = file
23
+ # plugin.security.file.certificate = /etc/puppetlabs/puppet/ssl/certs/<certname>.pem
24
+ # plugin.security.file.key = /etc/puppetlabs/puppet/ssl/private_keys/<certname>.pem
25
+ # plugin.security.file.ca = /etc/puppetlabs/puppet/ssl/certs/ca.pem
@@ -1,5 +1,6 @@
1
1
  require 'json'
2
2
  require 'open3'
3
+ require 'openssl'
3
4
  require 'smart_proxy_openbolt/executor'
4
5
  require 'smart_proxy_openbolt/error'
5
6
 
@@ -7,7 +8,7 @@ module Proxy::OpenBolt
7
8
  extend ::Proxy::Util
8
9
  extend ::Proxy::Log
9
10
 
10
- TRANSPORTS = ['ssh', 'winrm'].freeze
11
+ TRANSPORTS = ['ssh', 'winrm', 'choria'].freeze
11
12
  # The key should be exactly the flag name passed to OpenBolt
12
13
  # Type must be :boolean, :string, or an array of acceptable string values
13
14
  # Transport must be an array of transport types it applies to. This is
@@ -25,25 +26,25 @@ module Proxy::OpenBolt
25
26
  },
26
27
  'log-level' => {
27
28
  :type => ['error', 'warning', 'info', 'debug', 'trace'],
28
- :transport => ['ssh', 'winrm'],
29
+ :transport => ['ssh', 'winrm', 'choria'],
29
30
  :sensitive => false,
30
31
  :description => 'Set the log level during OpenBolt execution.',
31
32
  },
32
33
  'verbose' => {
33
34
  :type => :boolean,
34
- :transport => ['ssh', 'winrm'],
35
+ :transport => ['ssh', 'winrm', 'choria'],
35
36
  :sensitive => false,
36
37
  :description => 'Run the OpenBolt command with the --verbose flag. This prints additional information during OpenBolt execution and will print any out::verbose plan statements.',
37
38
  },
38
39
  'noop' => {
39
40
  :type => :boolean,
40
- :transport => ['ssh', 'winrm'],
41
+ :transport => ['ssh', 'winrm', 'choria'],
41
42
  :sensitive => false,
42
43
  :description => 'Run the OpenBolt command with the --noop flag, which will make no changes to the target host.',
43
44
  },
44
45
  'tmpdir' => {
45
46
  :type => :string,
46
- :transport => ['ssh', 'winrm'],
47
+ :transport => ['ssh', 'winrm', 'choria'],
47
48
  :sensitive => false,
48
49
  :description => 'Directory to use for temporary files on target hosts during OpenBolt execution.',
49
50
  },
@@ -95,6 +96,84 @@ module Proxy::OpenBolt
95
96
  :sensitive => false,
96
97
  :description => 'Verify remote host SSL certificate when connecting to hosts via WinRM.',
97
98
  },
99
+ 'choria-task-agent' => {
100
+ :type => ['bolt_tasks', 'shell'],
101
+ :transport => ['choria'],
102
+ :sensitive => false,
103
+ :description => 'Choria agent used to execute tasks on target nodes. "bolt_tasks" runs tasks via the Choria bolt_tasks agent and "shell" runs them via the shell agent. Defaults to "bolt_tasks" when not specified.',
104
+ },
105
+ 'choria-config-file' => {
106
+ :type => :string,
107
+ :transport => ['choria'],
108
+ :sensitive => false,
109
+ :description => 'Path on the smart proxy host to the Choria client configuration file. This file must be readable by the foreman-proxy user. When blank, the proxy uses a built-in default.',
110
+ },
111
+ 'choria-mcollective-certname' => {
112
+ :type => :string,
113
+ :transport => ['choria'],
114
+ :sensitive => false,
115
+ :description => 'Override the MCollective certname for Choria client identity. When blank, the proxy derives this automatically from the SSL certificate.',
116
+ },
117
+ 'choria-ssl-ca' => {
118
+ :type => :string,
119
+ :transport => ['choria'],
120
+ :sensitive => false,
121
+ :description => 'Path on the smart proxy host to the CA certificate used to verify Choria brokers and peers. This file must be readable by the foreman-proxy user. Must be provided together with choria-ssl-cert and choria-ssl-key.',
122
+ },
123
+ 'choria-ssl-cert' => {
124
+ :type => :string,
125
+ :transport => ['choria'],
126
+ :sensitive => false,
127
+ :description => 'Path on the smart proxy host to the client SSL certificate used to authenticate with Choria. This file must be readable by the foreman-proxy user. Must be provided together with choria-ssl-ca and choria-ssl-key.',
128
+ },
129
+ 'choria-ssl-key' => {
130
+ :type => :string,
131
+ :transport => ['choria'],
132
+ :sensitive => false,
133
+ :description => 'Path on the smart proxy host to the client SSL private key used to authenticate with Choria. This key must be readable by the foreman-proxy user. Must be provided together with choria-ssl-ca and choria-ssl-cert.',
134
+ },
135
+ 'choria-collective' => {
136
+ :type => :string,
137
+ :transport => ['choria'],
138
+ :sensitive => false,
139
+ :description => 'Choria collective to route messages through.',
140
+ },
141
+ 'choria-puppet-environment' => {
142
+ :type => :string,
143
+ :transport => ['choria'],
144
+ :sensitive => false,
145
+ :description => 'Puppet environment reported to the Choria agent when executing tasks. Defaults to "production" when not specified. Typically matches the proxy\'s environment_path setting.',
146
+ },
147
+ 'choria-rpc-timeout' => {
148
+ :type => :string,
149
+ :transport => ['choria'],
150
+ :sensitive => false,
151
+ :description => 'Timeout in seconds for individual Choria RPC calls. Defaults to 30 when not specified.',
152
+ },
153
+ 'choria-task-timeout' => {
154
+ :type => :string,
155
+ :transport => ['choria'],
156
+ :sensitive => false,
157
+ :description => 'Timeout in seconds for a Choria task to complete on a target node. Defaults to 300 when not specified.',
158
+ },
159
+ 'choria-command-timeout' => {
160
+ :type => :string,
161
+ :transport => ['choria'],
162
+ :sensitive => false,
163
+ :description => 'Timeout in seconds for a Choria shell command to complete on a target node. Defaults to 60 when not specified.',
164
+ },
165
+ 'choria-brokers' => {
166
+ :type => :string,
167
+ :transport => ['choria'],
168
+ :sensitive => false,
169
+ :description => 'Comma-separated list of Choria broker addresses in host or host:port format (e.g. broker1:4222,broker2:4222). Port defaults to 4222 if omitted.',
170
+ },
171
+ 'choria-broker-timeout' => {
172
+ :type => :string,
173
+ :transport => ['choria'],
174
+ :sensitive => false,
175
+ :description => 'Timeout in seconds for establishing a connection to a Choria broker.',
176
+ },
98
177
  }.freeze
99
178
  SORTED_OPTIONS = OPENBOLT_OPTIONS.sort.to_h.freeze
100
179
 
@@ -241,9 +320,76 @@ module Proxy::OpenBolt
241
320
  # Normalize options, removing blank values
242
321
  options = normalize_values(options)
243
322
  logger.info("Normalized options: #{scrub(options, options.inspect)}")
244
- OPENBOLT_OPTIONS.each { |key, value| options[key] ||= value[:default] if value.key?(:default) }
323
+ OPENBOLT_OPTIONS.each { |key, meta| options[key] ||= meta[:default] if meta.key?(:default) }
245
324
  logger.info("Options with required defaults: #{scrub(options, options.inspect)}")
246
325
 
326
+ # Choria transport defaults: fill in config file, SSL certs, and
327
+ # certname when the user has not provided them.
328
+ if options['transport'] == 'choria'
329
+ user_provided_config = options.key?('choria-config-file')
330
+
331
+ unless user_provided_config
332
+ shipped_config = File.join(File.dirname(__FILE__), 'config', 'choria-client.conf')
333
+ if File.readable?(shipped_config)
334
+ options['choria-config-file'] = shipped_config
335
+ else
336
+ logger.warn("Choria: shipped config at #{shipped_config} is not readable " \
337
+ "(exists=#{File.exist?(shipped_config)}). Check package installation " \
338
+ "and foreman-proxy user permissions.")
339
+ end
340
+ end
341
+
342
+ if !user_provided_config
343
+ missing_ssl = []
344
+ missing_ssl << 'ssl_certificate' if Proxy::SETTINGS.ssl_certificate.to_s.strip.empty?
345
+ missing_ssl << 'ssl_private_key' if Proxy::SETTINGS.ssl_private_key.to_s.strip.empty?
346
+ missing_ssl << 'ssl_ca_file' if Proxy::SETTINGS.ssl_ca_file.to_s.strip.empty?
347
+
348
+ if missing_ssl.empty?
349
+ options['choria-ssl-cert'] ||= Proxy::SETTINGS.ssl_certificate
350
+ options['choria-ssl-key'] ||= Proxy::SETTINGS.ssl_private_key
351
+ options['choria-ssl-ca'] ||= Proxy::SETTINGS.ssl_ca_file
352
+ else
353
+ logger.warn("Choria: cannot default SSL from proxy settings, missing: #{missing_ssl.join(', ')}. " \
354
+ "Set choria-ssl-cert, choria-ssl-key, and choria-ssl-ca explicitly.")
355
+ end
356
+ elsif !options.key?('choria-ssl-cert')
357
+ logger.info('Choria: custom config file provided without SSL options. ' \
358
+ 'SSL settings will be read from the config file.')
359
+ end
360
+
361
+ unless options.key?('choria-mcollective-certname')
362
+ cert_path = options['choria-ssl-cert']
363
+ if cert_path.nil? && user_provided_config
364
+ logger.info('Choria: custom config file provided, certname will come from the config file or ' \
365
+ "default to '<user>.mcollective'. Set 'choria-mcollective-certname' if needed.")
366
+ elsif cert_path.nil?
367
+ logger.warn('Choria: no choria-ssl-cert available, cannot derive mcollective-certname.')
368
+ elsif !File.readable?(cert_path)
369
+ logger.warn("Choria: cannot derive mcollective-certname, cert at #{cert_path} is not readable. " \
370
+ "Set 'choria-mcollective-certname' explicitly or fix file permissions.")
371
+ else
372
+ begin
373
+ cert = OpenSSL::X509::Certificate.new(File.read(cert_path))
374
+ cn = cert.subject.to_a.find { |name, _, _| name == 'CN' }
375
+ if cn
376
+ options['choria-mcollective-certname'] = cn[1]
377
+ else
378
+ logger.warn("Choria: certificate at #{cert_path} has no CN. " \
379
+ "Set 'choria-mcollective-certname' explicitly.")
380
+ end
381
+ rescue OpenSSL::X509::CertificateError => e
382
+ raise Error.new(
383
+ message: "Cannot read Choria certificate at #{cert_path}: #{e.message}. " \
384
+ "Set 'choria-mcollective-certname' explicitly or fix the certificate file."
385
+ )
386
+ end
387
+ end
388
+ end
389
+
390
+ logger.info("Choria options after defaults: #{scrub(options, options.inspect)}")
391
+ end
392
+
247
393
  # Validate option types
248
394
  options = options.to_h do |key, value|
249
395
  type = OPENBOLT_OPTIONS[key][:type]
@@ -1,5 +1,5 @@
1
1
  module Proxy
2
2
  module OpenBolt
3
- VERSION = '1.1.0'.freeze
3
+ VERSION = '1.2.0'.freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_proxy_openbolt
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Overlook InfraTech
@@ -42,6 +42,7 @@ files:
42
42
  - bundler.d/openbolt.rb
43
43
  - lib/smart_proxy_openbolt.rb
44
44
  - lib/smart_proxy_openbolt/api.rb
45
+ - lib/smart_proxy_openbolt/config/choria-client.conf
45
46
  - lib/smart_proxy_openbolt/error.rb
46
47
  - lib/smart_proxy_openbolt/executor.rb
47
48
  - lib/smart_proxy_openbolt/http_config.ru