From 210d00c99d2be369f79a1b4966bff51690d0facf Mon Sep 17 00:00:00 2001 From: Jeffrey Clark Date: Wed, 22 Nov 2023 01:01:02 -0600 Subject: [PATCH] (maint) add ed25519 support rubygems has compiled versions of bcrypt_pbkdf 1.1.0 for windows addresses #1987 --- bolt.gemspec | 2 ++ documentation/bolt_known_issues.md | 31 -------------------- spec/Dockerfile | 6 ++-- spec/bolt_server/app_integration_spec.rb | 37 +++++++++++++++--------- spec/fixtures/keys/id_ed25519 | 8 +++++ spec/fixtures/keys/id_ed25519.pub | 1 + spec/integration/transport/ssh_spec.rb | 9 ++++++ spec/unit/executor_spec.rb | 4 +-- 8 files changed, 50 insertions(+), 48 deletions(-) create mode 100644 spec/fixtures/keys/id_ed25519 create mode 100644 spec/fixtures/keys/id_ed25519.pub diff --git a/bolt.gemspec b/bolt.gemspec index 8f6fdb8e1b..b7767359ce 100644 --- a/bolt.gemspec +++ b/bolt.gemspec @@ -45,8 +45,10 @@ Gem::Specification.new do |spec| spec.add_dependency "addressable", '~> 2.5' spec.add_dependency "aws-sdk-ec2", '~> 1' + spec.add_dependency "bcrypt_pbkdf", ">= 1.1", "< 2.0" spec.add_dependency "CFPropertyList", ">= 2.2" spec.add_dependency "concurrent-ruby", "~> 1.0" + spec.add_dependency "ed25519", ">= 1.3", "< 2.0" spec.add_dependency "ffi", ">= 1.9.25", "< 2.0.0" spec.add_dependency "hiera-eyaml", "~> 3" spec.add_dependency "jwt", "~> 2.2" diff --git a/documentation/bolt_known_issues.md b/documentation/bolt_known_issues.md index ae7c769150..5045987ec1 100644 --- a/documentation/bolt_known_issues.md +++ b/documentation/bolt_known_issues.md @@ -73,37 +73,6 @@ command line with the `--password` option. - [#1986 - Commands fail if in a remote session to Windows](https://github.com/puppetlabs/bolt/issues/1986) -## Unable to authenticate with ed25519 keys over SSH transport on Windows - -By default, Bolt uses the `net-ssh` Ruby libary to connect to targets over SSH. -The `net-ssh` library requires the `ed25519` and `bcrypt_pbkdf` gems as -dependencies, which are not supported in Bolt's packaging process due to issues -with compiling native extensions. - -Attempting to authenticate with ed25519 keys over SSH on Windows will result -in an error message similar to this: - -``` -unsupported key type `ssh-ed25519' - net-ssh requires the following gems for ed25519 support: - * ed25519 (>= 1.2, < 2.0) - * bcrypt_pbkdf (>= 1.0, < 2.0) -``` - -A workaround is to use native SSH when you need to authenticate with ed25519 -keys. When native SSH is enabled, Bolt will use a specified SSH client to -connect to targets instead of the `net-ssh` Ruby library. To learn more about -native SSH, see [native SSH -transport](experimental_features.md#native-ssh-transport). - -🧪 Native SSH is -experimental and might change in future minor (y) releases. - -📖 **Related issues** - -- [#1987 - Unable to authenticate with ed25519 keys over SSH transport - on Windows](https://github.com/puppetlabs/bolt/issues/1987) - ## 🧪 Limited Kerberos support over WinRM 🧪 Authenticating with Kerberos over WinRM is considered experimental and is diff --git a/spec/Dockerfile b/spec/Dockerfile index 8bfb0bb098..f1976b871f 100644 --- a/spec/Dockerfile +++ b/spec/Dockerfile @@ -28,7 +28,8 @@ RUN adduser bolt sudo RUN mkdir -p /home/bolt/.ssh COPY fixtures/keys/id_rsa.pub /home/bolt/.ssh/id_rsa.pub -COPY fixtures/keys/id_rsa.pub /home/bolt/.ssh/authorized_keys +COPY fixtures/keys/id_ed25519.pub /home/bolt/.ssh/id_ed25519.pub +RUN cat /home/bolt/.ssh/*.pub > /home/bolt/.ssh/authorized_keys RUN chmod 700 /home/bolt/.ssh RUN chmod 600 /home/bolt/.ssh/authorized_keys RUN chown -R bolt:sudo /home/bolt @@ -41,7 +42,8 @@ RUN echo test | chsh -s /bin/bash test RUN mkdir -p /home/test/.ssh COPY fixtures/keys/id_rsa.pub /home/test/.ssh/id_rsa.pub -COPY fixtures/keys/id_rsa.pub /home/test/.ssh/authorized_keys +COPY fixtures/keys/id_ed25519.pub /home/test/.ssh/id_ed25519.pub +RUN cat /home/test/.ssh/*.pub > /home/test/.ssh/authorized_keys RUN chmod 700 /home/test/.ssh RUN chmod 600 /home/test/.ssh/authorized_keys RUN chown -R test:sudo /home/test diff --git a/spec/bolt_server/app_integration_spec.rb b/spec/bolt_server/app_integration_spec.rb index ff68f9c40d..2e7e86f4db 100644 --- a/spec/bolt_server/app_integration_spec.rb +++ b/spec/bolt_server/app_integration_spec.rb @@ -44,20 +44,31 @@ def app expect(result['value']['_output'].chomp).to match(/\w+ got passed the message: Hello!/) end - it 'runs an echo task using a private key' do - private_key = ENV['BOLT_SSH_KEY'] || Dir["spec/fixtures/keys/id_rsa"][0] - private_key_content = File.read(private_key) - target = conn_target('ssh', options: { 'private-key' => { 'key-data' => private_key_content } }) - body = build_task_request('sample::echo', - target, - message: "Hello!") + %w[env rsa ed25519].each do |key_type| + next if key_type == 'env' && ENV['BOLT_SSH_KEY'].nil? + + context "runs an echo task using an #{key_type} private key" do + let(:private_key) do + key_type == 'env' ? ENV['BOLT_SSH_KEY'] : Dir["spec/fixtures/keys/id_#{key_type}"][0] + end + + it do + private_key_content = File.read(private_key) + target = conn_target('ssh', options: { 'private-key' => { 'key-data' => private_key_content } }) + body = build_task_request('sample::echo', + target, + message: "Hello!") + + post path, JSON.generate(body), 'CONTENT_TYPE' => 'text/json' + expect(last_response).to be_ok + expect(last_response.status).to eq(200) + result = JSON.parse(last_response.body) + expect(result).to include('status' => 'success') + expect(result['value']['_output'].chomp).to match(/\w+ got passed the message: Hello!/) + end + end - post path, JSON.generate(body), 'CONTENT_TYPE' => 'text/json' - expect(last_response).to be_ok - expect(last_response.status).to eq(200) - result = JSON.parse(last_response.body) - expect(result).to include('status' => 'success') - expect(result['value']['_output'].chomp).to match(/\w+ got passed the message: Hello!/) + break if key_type == 'env' end it 'runs a shareable task' do diff --git a/spec/fixtures/keys/id_ed25519 b/spec/fixtures/keys/id_ed25519 new file mode 100644 index 0000000000..5c2cf5bec3 --- /dev/null +++ b/spec/fixtures/keys/id_ed25519 @@ -0,0 +1,8 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACDuFK/VaW7prqpNVY0H+bUTXsbDSeYg5XKlo0AitRXSmAAAALjUlsWQ1JbF +kAAAAAtzc2gtZWQyNTUxOQAAACDuFK/VaW7prqpNVY0H+bUTXsbDSeYg5XKlo0AitRXSmA +AAAECU+JslSRRf+EDh1yWFP6Hue8DrT0M8CGLp1UDKycZnfu4Ur9Vpbumuqk1VjQf5tRNe +xsNJ5iDlcqWjQCK1FdKYAAAAMWplZmZyZXkuY2xhcmtAamVmZnJleWNsYXJrcy1WaXJ0dW +FsLU1hY2hpbmUubG9jYWwBAgME +-----END OPENSSH PRIVATE KEY----- diff --git a/spec/fixtures/keys/id_ed25519.pub b/spec/fixtures/keys/id_ed25519.pub new file mode 100644 index 0000000000..710c64b638 --- /dev/null +++ b/spec/fixtures/keys/id_ed25519.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO4Ur9Vpbumuqk1VjQf5tRNexsNJ5iDlcqWjQCK1FdKY testin@tester.son diff --git a/spec/integration/transport/ssh_spec.rb b/spec/integration/transport/ssh_spec.rb index 95bf013ff1..770ca0f6c8 100644 --- a/spec/integration/transport/ssh_spec.rb +++ b/spec/integration/transport/ssh_spec.rb @@ -32,6 +32,7 @@ let(:bash_user) { 'test' } let(:bash_password) { 'test' } let(:key) { conn_info('ssh')[:key] } + let(:ed25519_key) { File.expand_path(File.join(__dir__, '..', '..', 'fixtures/keys/id_ed25519')) } let(:command) { "pwd" } let(:no_host_key_check) { { 'host-key-check' => false, user: user, password: password } } @@ -208,6 +209,14 @@ def make_target(host_: hostname, port_: port) ).to eq(true) end end + + context "with ed25519 private key" do + let(:transport_config) { super().merge({ 'private-key' => ed25519_key }) } + + it "executes a command on a host" do + expect(ssh.run_command(target, command).value['stdout']).to eq("/home/#{user}\n") + end + end end context "when executing with private key data" do diff --git a/spec/unit/executor_spec.rb b/spec/unit/executor_spec.rb index ecd811c0e0..293b9aa5b6 100644 --- a/spec/unit/executor_spec.rb +++ b/spec/unit/executor_spec.rb @@ -571,7 +571,7 @@ def mock_node_results expect(ssh) .to receive(:with_connection) .and_raise( - NotImplementedError.new('ed25519 is not supported') + NotImplementedError.new('something is not supported') ) end @@ -580,7 +580,7 @@ def mock_node_results results.each do |result| expect(result.error_hash['kind']).to eq('puppetlabs.tasks/exception-error') - expect(result.error_hash['msg']).to eq('ed25519 is not supported') + expect(result.error_hash['msg']).to eq('something is not supported') end expect(collector.events.count).to eq(10)