Add ssl_verify config option to fix certificate errors

Raw OpenSSL sockets don't find the system CA bundle the same way Net::HTTP
does. ssl_verify (default: true) is now a config setting that applies to
both HTTP REST calls and WebSocket connections. Set via `portainer-cli configure`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-22 13:07:07 +01:00
parent a1015366a9
commit c59deca51e
6 changed files with 20 additions and 12 deletions
+1 -1
View File
@@ -60,7 +60,7 @@ module PortainerCli
http = Net::HTTP.new(uri.host, uri.port) http = Net::HTTP.new(uri.host, uri.port)
if uri.scheme == 'https' if uri.scheme == 'https'
http.use_ssl = true http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER http.verify_mode = @config.ssl_verify ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
end end
http.open_timeout = 10 http.open_timeout = 10
http.read_timeout = 30 http.read_timeout = 30
+5
View File
@@ -32,6 +32,11 @@ module PortainerCli
@config.api_key = prompt_secret("API key", @config.api_key) @config.api_key = prompt_secret("API key", @config.api_key)
end end
puts
current_verify = @config.ssl_verify.nil? ? true : @config.ssl_verify
verify_choice = prompt("Verify SSL certificates? [y/n]", current_verify ? "y" : "n")
@config.ssl_verify = (verify_choice.downcase != "n")
@config.save @config.save
puts puts
puts green("Configuration saved to #{Config::CONFIG_FILE}") puts green("Configuration saved to #{Config::CONFIG_FILE}")
+1 -1
View File
@@ -42,7 +42,7 @@ module PortainerCli
send_resize(endpoint_id, exec_id) if $stdout.tty? send_resize(endpoint_id, exec_id) if $stdout.tty?
trap('SIGWINCH') { send_resize(endpoint_id, exec_id) } if $stdout.tty? trap('SIGWINCH') { send_resize(endpoint_id, exec_id) } if $stdout.tty?
WebsocketExec.new(ws_url, @config.auth_header).run WebsocketExec.new(ws_url, @config.auth_header, ssl_verify: @config.ssl_verify).run
$stderr.puts dim("\r\nSession ended.") $stderr.puts dim("\r\nSession ended.")
rescue PortainerCli::Client::ApiError => e rescue PortainerCli::Client::ApiError => e
+1 -1
View File
@@ -36,7 +36,7 @@ module PortainerCli
send_resize(endpoint_id, exec_id) if $stdout.tty? send_resize(endpoint_id, exec_id) if $stdout.tty?
trap('SIGWINCH') { send_resize(endpoint_id, exec_id) } if $stdout.tty? trap('SIGWINCH') { send_resize(endpoint_id, exec_id) } if $stdout.tty?
WebsocketExec.new(ws_url, @config.auth_header).run WebsocketExec.new(ws_url, @config.auth_header, ssl_verify: @config.ssl_verify).run
$stderr.puts dim("\r\nSession ended.") $stderr.puts dim("\r\nSession ended.")
rescue PortainerCli::Client::ApiError => e rescue PortainerCli::Client::ApiError => e
+3 -1
View File
@@ -8,7 +8,7 @@ module PortainerCli
CONFIG_DIR = File.expand_path('~/.portainer-cli').freeze CONFIG_DIR = File.expand_path('~/.portainer-cli').freeze
CONFIG_FILE = File.join(CONFIG_DIR, 'config.yml').freeze CONFIG_FILE = File.join(CONFIG_DIR, 'config.yml').freeze
attr_accessor :url, :token, :api_key attr_accessor :url, :token, :api_key, :ssl_verify
def self.load def self.load
new.tap(&:read) new.tap(&:read)
@@ -21,6 +21,7 @@ module PortainerCli
@url = data['url'] @url = data['url']
@token = data['token'] @token = data['token']
@api_key = data['api_key'] @api_key = data['api_key']
@ssl_verify = data.fetch('ssl_verify', true)
end end
def save def save
@@ -29,6 +30,7 @@ module PortainerCli
data['url'] = @url if @url data['url'] = @url if @url
data['token'] = @token if @token data['token'] = @token if @token
data['api_key'] = @api_key if @api_key data['api_key'] = @api_key if @api_key
data['ssl_verify'] = @ssl_verify unless @ssl_verify.nil?
File.write(CONFIG_FILE, YAML.dump(data)) File.write(CONFIG_FILE, YAML.dump(data))
File.chmod(0o600, CONFIG_FILE) File.chmod(0o600, CONFIG_FILE)
end end
+3 -2
View File
@@ -10,9 +10,10 @@ module PortainerCli
class WebsocketExec class WebsocketExec
READ_SIZE = 4096 READ_SIZE = 4096
def initialize(url, auth_header) def initialize(url, auth_header, ssl_verify: true)
@url = URI.parse(url) @url = URI.parse(url)
@auth_header = auth_header @auth_header = auth_header
@ssl_verify = ssl_verify
end end
def run def run
@@ -33,7 +34,7 @@ module PortainerCli
if @url.scheme == 'wss' if @url.scheme == 'wss'
ctx = OpenSSL::SSL::SSLContext.new ctx = OpenSSL::SSL::SSLContext.new
ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER ctx.verify_mode = @ssl_verify ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
ssl = OpenSSL::SSL::SSLSocket.new(raw, ctx) ssl = OpenSSL::SSL::SSLSocket.new(raw, ctx)
ssl.hostname = @url.host ssl.hostname = @url.host
ssl.sync_close = true ssl.sync_close = true