Class: Chef::Knife::Bootstrap
- Inherits:
-
Chef::Knife
- Object
- Chef::Knife
- Chef::Knife::Bootstrap
- Includes:
- DataBagSecretOptions, LicenseAcceptance::CLIFlags::MixlibCLI
- Defined in:
- lib/chef/knife/bootstrap.rb,
lib/chef/knife/bootstrap/client_builder.rb,
lib/chef/knife/bootstrap/train_connector.rb,
lib/chef/knife/bootstrap/chef_vault_handler.rb
Defined Under Namespace
Classes: ChefVaultHandler, ClientBuilder, RemoteExecutionFailed, TrainConnector
Constant Summary
Constants inherited from Chef::Knife
CHEF_ORGANIZATION_MANAGEMENT, KNIFE_ROOT, OFFICIAL_PLUGINS, OPSCODE_HOSTED_CHEF_ACCESS_CONTROL, VERSION
Instance Attribute Summary collapse
-
#connection ⇒ Object
readonly
Returns the value of attribute connection.
Attributes inherited from Chef::Knife
Instance Method Summary collapse
-
#base_opts ⇒ Object
Common configuration for all protocols.
-
#bootstrap_command(remote_path) ⇒ Object
build the command string for bootstrapping.
-
#bootstrap_context ⇒ Object
Establish bootstrap context for template rendering.
-
#bootstrap_run_command(cmd) ⇒ Object
Actual bootstrap command to be run on the node.
-
#bootstrap_template ⇒ String
The CLI specific bootstrap template or the default.
-
#check_eula_license ⇒ Object
(also: #check_license)
Determine if we need to accept the Chef Infra license locally in order to successfully bootstrap the remote node.
- #chef_vault_handler ⇒ Object
- #client_builder ⇒ Object
-
#config_value(key, fallback_key = nil, default = nil) ⇒ Object
This is for deprecating config options.
- #connect! ⇒ Object
-
#connection_opts(reset: false, sudo_pass: nil) ⇒ Object
host via train.
-
#connection_protocol ⇒ Object
url values override CLI flags, if you provide both we’ll use the one that you gave in the URL.
-
#default_bootstrap_template ⇒ String
The default bootstrap template to use to bootstrap a server.
- #do_connect(conn_options) ⇒ Object
- #find_template ⇒ Object
- #first_boot_attributes ⇒ Object
-
#force_ssh_password_opts(password) ⇒ Object
Config overrides to force password auth.
- #force_winrm_password_opts(password) ⇒ Object
- #gateway_opts ⇒ Object
- #handle_ssh_error(e) ⇒ Object
- #host_descriptor ⇒ Object
- #host_verify_opts ⇒ Object
- #perform_bootstrap(remote_bootstrap_script_path) ⇒ Object
-
#plugin_create_instance! ⇒ TrueClass
Create the server that we will bootstrap, if necessary.
-
#plugin_finalize ⇒ void
Perform any teardown or cleanup necessary by the plugin.
-
#plugin_setup! ⇒ TrueClass
Perform any setup necessary by the plugin.
-
#plugin_validate_options! ⇒ TrueClass
Validate any additional options.
- #register_client ⇒ Object
- #render_template ⇒ Object
- #run ⇒ Object
- #secret ⇒ Object
-
#server_name ⇒ String
The server_name is the DNS or IP we are going to connect to, it is not necessarily the node name, the fqdn, or the hostname of the server.
- #ssh? ⇒ Boolean
-
#ssh_gateway ⇒ Object
SSH Authentication.
- #ssh_identity_opts ⇒ Object
- #ssh_opts ⇒ Object
- #ssh_verify_host_key ⇒ Object
-
#sudo_opts(sudo_pass) ⇒ Object
use_sudo - tells bootstrap to use the sudo command to run bootstrap use_sudo_password - tells bootstrap to use the sudo command to run bootstrap and to use the password specified with –password TODO: I’d like to make our sudo options sane: –sudo (bool) - use sudo –sudo-password PASSWORD (default: :password) - use this password for sudo –sudo-options “opt,opt,opt” to pass into sudo –sudo-command COMMAND sudo command other than sudo REVIEW NOTE: knife bootstrap did not pull sudo values from Chef::Config, should we change that for consistency?.
- #upload_bootstrap(content) ⇒ Object
-
#validate_first_boot_attributes! ⇒ Object
Fail if both first_boot_attributes and first_boot_attributes_from_file are set.
-
#validate_name_args! ⇒ Object
fail if the server_name is nil.
-
#validate_policy_options! ⇒ TrueClass
Ensure options are valid by checking policyfile values.
-
#validate_protocol! ⇒ TrueClass
Ensure a valid protocol is provided for target host connection.
-
#validate_winrm_transport_opts! ⇒ Object
Fail if using plaintext auth without ssl because this can expose keys in plaintext on the wire.
-
#warn_on_short_session_timeout ⇒ Object
If session_timeout is too short, it is likely a holdover from “–winrm-session-timeout” which used minutes as its unit, instead of seconds.
- #winrm? ⇒ Boolean
-
#winrm_auth_method ⇒ Object
FIXME: someone needs to clean this up properly: github.com/chef/chef/issues/9645 This code is deliberately left without an abstraction around deprecating the config options to avoid knife plugins from using those methods (which will need to be deprecated and break them) via inheritance (ruby does not have a true ‘private` so the lack of any inheritable implementation is because of that).
- #winrm_opts ⇒ Object
- #winrm_warn_no_ssl_verification ⇒ Object
Methods included from DataBagSecretOptions
#encryption_secret_provided?, #encryption_secret_provided_ignore_encrypt_flag?, included, #read_secret, #validate_secrets
Methods inherited from Chef::Knife
#api_key, #apply_computed_config, category, chef_config_dir, common_name, #config_file_defaults, #config_file_settings, config_loader, #config_source, #configure_chef, #create_object, #delete_object, dependency_loaders, deps, #format_rest_error, guess_category, #humanize_exception, #humanize_http_exception, inherited, #initialize, list_commands, load_commands, load_config, load_deps, #maybe_setup_fips, #merge_configs, msg, #noauth_rest, #parse_options, reset_config_loader!, reset_subcommands!, #rest, #root_rest, run, #run_with_pretty_exceptions, #server_url, #show_usage, snake_case_name, subcommand_category, subcommand_class_from, subcommand_files, subcommand_loader, subcommands, subcommands_by_category, #test_mandatory_field, ui, unnamed?, use_separate_defaults?, #username
Constructor Details
This class inherits a constructor from Chef::Knife
Instance Attribute Details
#connection ⇒ Object (readonly)
Returns the value of attribute connection.
429 430 431 |
# File 'lib/chef/knife/bootstrap.rb', line 429 def connection @connection end |
Instance Method Details
#base_opts ⇒ Object
Common configuration for all protocols
942 943 944 945 946 947 948 949 950 951 952 953 |
# File 'lib/chef/knife/bootstrap.rb', line 942 def base_opts port = config_for_protocol(:port) user = config_for_protocol(:user) {}.tap do |opts| opts[:logger] = Chef::Log opts[:password] = config[:connection_password] if config.key?(:connection_password) opts[:user] = user if user opts[:max_wait_until_ready] = config[:max_wait].to_i unless config[:max_wait].nil? # TODO - when would we need to provide rdp_port vs port? Or are they not mutually exclusive? opts[:port] = port if port end end |
#bootstrap_command(remote_path) ⇒ Object
build the command string for bootstrapping
1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 |
# File 'lib/chef/knife/bootstrap.rb', line 1127 def bootstrap_command(remote_path) if connection.windows? "cmd.exe /C #{remote_path}" else cmd = "sh #{remote_path}" if config[:su_user] # su - USER is subject to required an interactive console # Otherwise, it will raise: su: must be run from a terminal (pty: true) cmd = "su - #{config[:su_user]} -c '#{cmd}'" cmd = "sudo " << cmd if config[:use_sudo] end cmd end end |
#bootstrap_context ⇒ Object
Establish bootstrap context for template rendering. Requires connection to be a live connection in order to determine the correct platform.
545 546 547 548 549 550 551 552 553 554 |
# File 'lib/chef/knife/bootstrap.rb', line 545 def bootstrap_context @bootstrap_context ||= if connection.windows? require_relative "core/windows_bootstrap_context" Knife::Core::WindowsBootstrapContext.new(config, config[:run_list], Chef::Config, secret) else require_relative "core/bootstrap_context" Knife::Core::BootstrapContext.new(config, config[:run_list], Chef::Config, secret) end end |
#bootstrap_run_command(cmd) ⇒ Object
Actual bootstrap command to be run on the node. Handles recursive calls if su USER failed to authenticate.
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 |
# File 'lib/chef/knife/bootstrap.rb', line 625 def bootstrap_run_command(cmd) r = connection.run_command(cmd) do |data, channel| ui.msg("#{ui.color(" [#{connection.hostname}]", :cyan)} #{data}") channel.send_data("#{config[:su_password] || config[:connection_password]}\n") if data.match?("Password:") end if r.exit_status != 0 ui.error("The following error occurred on #{server_name}:") ui.error("#{r.stdout} #{r.stderr}".strip) exit(r.exit_status) end rescue Train::UserError => e limit ||= 0 if e.reason == :bad_su_user_password && limit < 3 limit += 1 ui.warn("Failed to authenticate su - #{config[:su_user]} to #{server_name}") config[:su_password] = ui.ask("Enter password for su - #{config[:su_user]}@#{server_name}:", echo: false) retry else raise end end |
#bootstrap_template ⇒ String
Returns The CLI specific bootstrap template or the default.
501 502 503 504 |
# File 'lib/chef/knife/bootstrap.rb', line 501 def bootstrap_template # Allow passing a bootstrap template or use the default config[:bootstrap_template] || default_bootstrap_template end |
#check_eula_license ⇒ Object Also known as: check_license
Determine if we need to accept the Chef Infra license locally in order to successfully bootstrap the remote node. Remote ‘chef-client’ run will fail if it is >= 15 and the license is not accepted locally.
457 458 459 460 461 462 463 464 465 466 467 |
# File 'lib/chef/knife/bootstrap.rb', line 457 def check_eula_license Chef::Log.debug("Checking if we need to accept Chef license to bootstrap node") version = config[:bootstrap_version] || Chef::VERSION.split(".").first acceptor = LicenseAcceptance::Acceptor.new(logger: Chef::Log, provided: Chef::Config[:chef_license]) if acceptor.license_required?("chef", version) Chef::Log.debug("License acceptance required for chef version: #{version}") license_id = acceptor.id_from_mixlib("chef") acceptor.check_and_persist(license_id, version) Chef::Config[:chef_license] ||= acceptor.acceptance_value end end |
#chef_vault_handler ⇒ Object
448 449 450 451 452 453 |
# File 'lib/chef/knife/bootstrap.rb', line 448 def chef_vault_handler @chef_vault_handler ||= Chef::Knife::Bootstrap::ChefVaultHandler.new( config: config, ui: ui ) end |
#client_builder ⇒ Object
440 441 442 443 444 445 446 |
# File 'lib/chef/knife/bootstrap.rb', line 440 def client_builder @client_builder ||= Chef::Knife::Bootstrap::ClientBuilder.new( chef_config: Chef::Config, config: config, ui: ui ) end |
#config_value(key, fallback_key = nil, default = nil) ⇒ Object
This is for deprecating config options. The fallback_key can be used to pull an old knife config option out of the config file when the cli value has been renamed. This is different from the deprecated cli values, since these are for config options that have no corresponding cli value.
DO NOT USE - this whole API is considered deprecated
1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 |
# File 'lib/chef/knife/bootstrap.rb', line 1105 def config_value(key, fallback_key = nil, default = nil) Chef.deprecated(:knife_bootstrap_apis, "Use of config_value is deprecated. Knife plugin authors should access the config hash directly, which does correct merging of cli and config options.") if config.key?(key) # the first key is the primary key so we check the merged hash first config[key] elsif config.key?(fallback_key) # we get the old config option here (the deprecated cli option shouldn't exist) config[fallback_key] else default end end |
#connect! ⇒ Object
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 |
# File 'lib/chef/knife/bootstrap.rb', line 648 def connect! ui.info("Connecting to #{ui.color(server_name, :bold)} using #{connection_protocol}") opts ||= connection_opts.dup do_connect(opts) rescue Train::Error => e # We handle these by message text only because train only loads the # transports and protocols that it needs - so the exceptions may not be defined, # and we don't want to require files internal to train. if e. =~ /fingerprint (\S+) is unknown for "(.+)"/ # Train::Transports::SSHFailed fingerprint = $1 hostname, ip = $2.split(",") # TODO: convert the SHA256 base64 value to hex with colons # 'ssh' example output: # RSA key fingerprint is e5:cb:c0:e2:21:3b:12:52:f8:ce:cb:00:24:e2:0c:92. # ECDSA key fingerprint is 5d:67:61:08:a9:d7:01:fd:5e:ae:7e:09:40:ef:c0:3c. # will exit 3 on N ui.confirm <<~EOM The authenticity of host '#{hostname} (#{ip})' can't be established. fingerprint is #{fingerprint}. Are you sure you want to continue connecting EOM # FIXME: this should save the key to known_hosts but doesn't appear to be config[:ssh_verify_host_key] = :accept_new conn_opts = connection_opts(reset: true, sudo_pass: opts[:password]) opts.merge! conn_opts retry elsif (ssh? && e.cause && e.cause.class == Net::SSH::AuthenticationFailed) || (ssh? && e.class == Train::ClientError && e.reason == :no_ssh_password_or_key_available) if connection.password_auth? raise else ui.warn("Failed to authenticate #{opts[:user]} to #{server_name} - trying password auth") password = ui.ask("Enter password for #{opts[:user]}@#{server_name}:", echo: false) end opts.merge! force_ssh_password_opts(password) retry else raise end rescue RuntimeError => e if winrm? && e. == "password is a required option" if connection.password_auth? raise else ui.warn("Failed to authenticate #{opts[:user]} to #{server_name} - trying password auth") password = ui.ask("Enter password for #{opts[:user]}@#{server_name}:", echo: false) end opts.merge! force_winrm_password_opts(password) retry else raise end end |
#connection_opts(reset: false, sudo_pass: nil) ⇒ Object
host via train
919 920 921 922 923 924 925 926 927 928 929 930 931 |
# File 'lib/chef/knife/bootstrap.rb', line 919 def connection_opts(reset: false, sudo_pass: nil ) return @connection_opts unless @connection_opts.nil? || reset == true @connection_opts = {} @connection_opts.merge! base_opts @connection_opts.merge! host_verify_opts @connection_opts.merge! gateway_opts @connection_opts.merge! sudo_opts(sudo_pass) @connection_opts.merge! winrm_opts @connection_opts.merge! ssh_opts @connection_opts.merge! ssh_identity_opts @connection_opts end |
#connection_protocol ⇒ Object
url values override CLI flags, if you provide both we’ll use the one that you gave in the URL.
708 709 710 711 712 713 714 |
# File 'lib/chef/knife/bootstrap.rb', line 708 def connection_protocol return @connection_protocol if @connection_protocol from_url = host_descriptor =~ %r{^(.*)://} ? $1 : nil from_knife = config[:connection_protocol] @connection_protocol = from_url || from_knife || "ssh" end |
#default_bootstrap_template ⇒ String
The default bootstrap template to use to bootstrap a server. This is a public API hook which knife plugins use or inherit and override.
477 478 479 480 481 482 483 |
# File 'lib/chef/knife/bootstrap.rb', line 477 def default_bootstrap_template if connection.windows? "windows-chef-client-msi" else "chef-full" end end |
#do_connect(conn_options) ⇒ Object
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 |
# File 'lib/chef/knife/bootstrap.rb', line 716 def do_connect() @connection = TrainConnector.new(host_descriptor, connection_protocol, ) connection.connect! rescue Train::UserError => e limit ||= 1 if !.key?(:pty) && e.reason == :sudo_no_tty ui.warn("#{e.} - trying with pty request") [:pty] = true # ensure we can talk to systems with requiretty set true in sshd config retry elsif e.reason == :sudo_missing_terminal ui.error "Sudo password is required for this operation. Please enter password using -P or --ssh-password option" elsif config[:use_sudo_password] && (e.reason == :sudo_password_required || e.reason == :bad_sudo_password) && limit < 3 ui.warn("Failed to authenticate #{[:user]} to #{server_name} - #{e.} \n sudo: #{limit} incorrect password attempt") sudo_password = ui.ask("Enter sudo password for #{[:user]}@#{server_name}:", echo: false) limit += 1 [:sudo_password] = sudo_password retry else raise end end |
#find_template ⇒ Object
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 |
# File 'lib/chef/knife/bootstrap.rb', line 506 def find_template template = bootstrap_template # Use the template directly if it's a path to an actual file if File.exist?(template) Chef::Log.trace("Using the specified bootstrap template: #{File.dirname(template)}") return template end # Otherwise search the template directories until we find the right one bootstrap_files = [] bootstrap_files << File.join(__dir__, "bootstrap/templates", "#{template}.erb") bootstrap_files << File.join(Knife.chef_config_dir, "bootstrap", "#{template}.erb") if Chef::Knife.chef_config_dir ChefConfig::PathHelper.home(".chef", "bootstrap", "#{template}.erb") { |p| bootstrap_files << p } bootstrap_files << Gem.find_files(File.join("chef", "knife", "bootstrap", "#{template}.erb")) bootstrap_files.flatten! template_file = Array(bootstrap_files).find do |bootstrap_template| Chef::Log.trace("Looking for bootstrap template in #{File.dirname(bootstrap_template)}") File.exist?(bootstrap_template) end unless template_file ui.info("Can not find bootstrap definition for #{template}") raise Errno::ENOENT end Chef::Log.trace("Found bootstrap template: #{template_file}") template_file end |
#first_boot_attributes ⇒ Object
556 557 558 |
# File 'lib/chef/knife/bootstrap.rb', line 556 def first_boot_attributes @config[:first_boot_attributes] || @config[:first_boot_attributes_from_file] || {} end |
#force_ssh_password_opts(password) ⇒ Object
Config overrides to force password auth.
1079 1080 1081 1082 1083 1084 1085 1086 1087 |
# File 'lib/chef/knife/bootstrap.rb', line 1079 def force_ssh_password_opts(password) { password: password, non_interactive: false, keys_only: false, key_files: [], auth_methods: %i{password keyboard_interactive}, } end |
#force_winrm_password_opts(password) ⇒ Object
1089 1090 1091 1092 1093 |
# File 'lib/chef/knife/bootstrap.rb', line 1089 def force_winrm_password_opts(password) { password: password, } end |
#gateway_opts ⇒ Object
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 |
# File 'lib/chef/knife/bootstrap.rb', line 1008 def gateway_opts opts = {} if config[:ssh_gateway] split = config[:ssh_gateway].split("@", 2) if split.length == 1 gw_host = split[0] else gw_user = split[0] gw_host = split[1] end gw_host, gw_port = gw_host.split(":", 2) # TODO - validate convertible port in config validation? gw_port = Integer(gw_port) rescue nil opts[:bastion_host] = gw_host opts[:bastion_user] = gw_user opts[:bastion_port] = gw_port end opts end |
#handle_ssh_error(e) ⇒ Object
704 |
# File 'lib/chef/knife/bootstrap.rb', line 704 def handle_ssh_error(e); end |
#host_descriptor ⇒ Object
485 486 487 |
# File 'lib/chef/knife/bootstrap.rb', line 485 def host_descriptor Array(@name_args).first end |
#host_verify_opts ⇒ Object
955 956 957 958 959 960 961 962 963 964 |
# File 'lib/chef/knife/bootstrap.rb', line 955 def host_verify_opts if winrm? { self_signed: config[:winrm_no_verify_cert] === true } elsif ssh? # Fall back to the old knife config key name for back compat. { verify_host_key: ssh_verify_host_key } else {} end end |
#perform_bootstrap(remote_bootstrap_script_path) ⇒ Object
617 618 619 620 621 |
# File 'lib/chef/knife/bootstrap.rb', line 617 def perform_bootstrap(remote_bootstrap_script_path) ui.info("Bootstrapping #{ui.color(server_name, :bold)}") cmd = bootstrap_command(remote_bootstrap_script_path) bootstrap_run_command(cmd) end |
#plugin_create_instance! ⇒ TrueClass
Create the server that we will bootstrap, if necessary
Plugins that subclass bootstrap, e.g. knife-ec2, can use this method to call out to an API to build an instance of the server we wish to bootstrap
855 856 857 |
# File 'lib/chef/knife/bootstrap.rb', line 855 def plugin_create_instance! true end |
#plugin_finalize ⇒ void
This method returns an undefined value.
Perform any teardown or cleanup necessary by the plugin
Plugins that subclass bootstrap, e.g. knife-ec2, can use this method to display a message or perform any cleanup
871 |
# File 'lib/chef/knife/bootstrap.rb', line 871 def plugin_finalize; end |
#plugin_setup! ⇒ TrueClass
Perform any setup necessary by the plugin
Plugins that subclass bootstrap, e.g. knife-ec2, can use this method to create connection objects
864 |
# File 'lib/chef/knife/bootstrap.rb', line 864 def plugin_setup!; end |
#plugin_validate_options! ⇒ TrueClass
Validate any additional options
Plugins that subclass bootstrap, e.g. knife-ec2, can use this method to validate any additional options before any other actions are executed
846 847 848 |
# File 'lib/chef/knife/bootstrap.rb', line 846 def true end |
#register_client ⇒ Object
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 |
# File 'lib/chef/knife/bootstrap.rb', line 596 def register_client # chef-vault integration must use the new client-side hawtness, otherwise to use the # new client-side hawtness, just delete your validation key. if chef_vault_handler.doing_chef_vault? || (Chef::Config[:validation_key] && !File.exist?(File.(Chef::Config[:validation_key]))) unless config[:chef_node_name] ui.error("You must pass a node name with -N when bootstrapping with user credentials") exit 1 end client_builder.run chef_vault_handler.run(client_builder.client) bootstrap_context.client_pem = client_builder.client_path else ui.warn "Performing legacy client registration with the validation key at #{Chef::Config[:validation_key]}..." ui.warn "Remove the key file or remove the 'validation_key' configuration option from your config.rb (knife.rb) to use more secure user credentials for client registration." end end |
#render_template ⇒ Object
560 561 562 563 564 565 566 |
# File 'lib/chef/knife/bootstrap.rb', line 560 def render_template require "erubis" unless defined?(Erubis) @config[:first_boot_attributes] = first_boot_attributes template_file = find_template template = File.read(template_file).chomp Erubis::Eruby.new(template).evaluate(bootstrap_context) end |
#run ⇒ Object
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 |
# File 'lib/chef/knife/bootstrap.rb', line 568 def run check_eula_license if ChefUtils::Dist::Org::ENFORCE_LICENSE fetch_license plugin_setup! validate_name_args! validate_protocol! validate_first_boot_attributes! validate_winrm_transport_opts! winrm_warn_no_ssl_verification warn_on_short_session_timeout plugin_create_instance! $stdout.sync = true connect! register_client content = render_template bootstrap_path = upload_bootstrap(content) perform_bootstrap(bootstrap_path) plugin_finalize ensure connection.del_file!(bootstrap_path) if connection && bootstrap_path end |
#secret ⇒ Object
538 539 540 |
# File 'lib/chef/knife/bootstrap.rb', line 538 def secret @secret ||= encryption_secret_provided_ignore_encrypt_flag? ? read_secret : nil end |
#server_name ⇒ String
The server_name is the DNS or IP we are going to connect to, it is not necessarily the node name, the fqdn, or the hostname of the server. This is a public API hook which knife plugins use or inherit and override.
494 495 496 497 498 |
# File 'lib/chef/knife/bootstrap.rb', line 494 def server_name if host_descriptor @server_name ||= host_descriptor.split("@").reverse[0] end end |
#ssh? ⇒ Boolean
937 938 939 |
# File 'lib/chef/knife/bootstrap.rb', line 937 def ssh? connection_protocol == "ssh" end |
#ssh_gateway ⇒ Object
SSH Authentication
119 120 121 122 |
# File 'lib/chef/knife/bootstrap.rb', line 119 option :ssh_gateway, short: "-G GATEWAY", long: "--ssh-gateway GATEWAY", description: "The SSH gateway." |
#ssh_identity_opts ⇒ Object
976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 |
# File 'lib/chef/knife/bootstrap.rb', line 976 def ssh_identity_opts opts = {} return opts if winrm? identity_file = config[:ssh_identity_file] if identity_file opts[:key_files] = [identity_file] # We only set keys_only based on the explicit ssh_identity_file; # someone may use a gateway key and still expect password auth # on the target. Similarly, someone may have a default key specified # in knife config, but have provided a password on the CLI. # REVIEW NOTE: this is a new behavior. Originally, ssh_identity_file # could only be populated from CLI options, so there was no need to check # for this. We will also set keys_only to false only if there are keys # and no password. # If both are present, train(via net/ssh) will prefer keys, falling back to password. # Reference: https://github.com/chef/chef/blob/main/lib/chef/knife/ssh.rb#L272 opts[:keys_only] = config.key?(:connection_password) == false else opts[:key_files] = [] opts[:keys_only] = false end gateway_identity_file = config[:ssh_gateway] ? config[:ssh_gateway_identity] : nil unless gateway_identity_file.nil? opts[:key_files] << gateway_identity_file end opts end |
#ssh_opts ⇒ Object
966 967 968 969 970 971 972 973 974 |
# File 'lib/chef/knife/bootstrap.rb', line 966 def ssh_opts opts = {} return opts if winrm? opts[:non_interactive] = true # Prevent password prompts from underlying net/ssh opts[:forward_agent] = (config[:ssh_forward_agent] === true) opts[:connection_timeout] = session_timeout opts end |
#ssh_verify_host_key ⇒ Object
758 759 760 |
# File 'lib/chef/knife/bootstrap.rb', line 758 def ssh_verify_host_key config.key?(:ssh_verify_host_key) ? config[:ssh_verify_host_key] : config.key?(:host_key_verify) ? config[:host_key_verify] : "always" # rubocop:disable Style/NestedTernaryOperator end |
#sudo_opts(sudo_pass) ⇒ Object
use_sudo - tells bootstrap to use the sudo command to run bootstrap use_sudo_password - tells bootstrap to use the sudo command to run bootstrap
and to use the password specified with --password
TODO: I’d like to make our sudo options sane: –sudo (bool) - use sudo –sudo-password PASSWORD (default: :password) - use this password for sudo –sudo-options “opt,opt,opt” to pass into sudo –sudo-command COMMAND sudo command other than sudo REVIEW NOTE: knife bootstrap did not pull sudo values from Chef::Config,
should we change that for consistency?
1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 |
# File 'lib/chef/knife/bootstrap.rb', line 1038 def sudo_opts(sudo_pass) return {} if winrm? opts = { sudo: false } if config[:use_sudo] opts[:sudo] = true if config[:use_sudo_password] opts[:sudo_password] = !config[:connection_password].nil? ? config[:connection_password] : sudo_pass end if config[:preserve_home] opts[:sudo_options] = "-H" end end opts end |
#upload_bootstrap(content) ⇒ Object
1118 1119 1120 1121 1122 1123 |
# File 'lib/chef/knife/bootstrap.rb', line 1118 def upload_bootstrap(content) script_name = connection.windows? ? "bootstrap.bat" : "bootstrap.sh" remote_path = connection.normalize_path(File.join(connection.temp_dir, script_name)) connection.upload_file_content!(content, remote_path) remote_path end |
#validate_first_boot_attributes! ⇒ Object
Fail if both first_boot_attributes and first_boot_attributes_from_file are set.
741 742 743 744 745 746 747 |
# File 'lib/chef/knife/bootstrap.rb', line 741 def validate_first_boot_attributes! if @config[:first_boot_attributes] && @config[:first_boot_attributes_from_file] raise Chef::Exceptions::BootstrapCommandInputError end true end |
#validate_name_args! ⇒ Object
fail if the server_name is nil
787 788 789 790 791 792 |
# File 'lib/chef/knife/bootstrap.rb', line 787 def validate_name_args! if server_name.nil? ui.error("Must pass an FQDN or ip to bootstrap") exit 1 end end |
#validate_policy_options! ⇒ TrueClass
Ensure options are valid by checking policyfile values.
The method call will cause the program to exit(1) if:
* Only one of --policy-name and --policy-group is specified
* Policyfile options are set and --run-list is set as well
801 802 803 804 805 806 807 808 809 |
# File 'lib/chef/knife/bootstrap.rb', line 801 def if ui.error("--policy-name and --policy-group must be specified together") exit 1 elsif policyfile_and_run_list_given? ui.error("Policyfile options and --run-list are exclusive") exit 1 end end |
#validate_protocol! ⇒ TrueClass
Ensure a valid protocol is provided for target host connection
The method call will cause the program to exit(1) if:
* Conflicting protocols are given via the target URI and the --protocol option
* The protocol is not a supported protocol
818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 |
# File 'lib/chef/knife/bootstrap.rb', line 818 def validate_protocol! from_cli = config[:connection_protocol] if from_cli && connection_protocol != from_cli # Hanging indent to align with the ERROR: prefix ui.error <<~EOM The URL '#{host_descriptor}' indicates protocol is '#{connection_protocol}' while the --protocol flag specifies '#{from_cli}'. Please include only one or the other. EOM exit 1 end unless SUPPORTED_CONNECTION_PROTOCOLS.include?(connection_protocol) ui.error <<~EOM Unsupported protocol '#{connection_protocol}'. Supported protocols are: #{SUPPORTED_CONNECTION_PROTOCOLS.join(" ")} EOM exit 1 end true end |
#validate_winrm_transport_opts! ⇒ Object
Fail if using plaintext auth without ssl because this can expose keys in plaintext on the wire. TODO test for this method TODO check that the protocol is valid.
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 |
# File 'lib/chef/knife/bootstrap.rb', line 766 def validate_winrm_transport_opts! return true unless winrm? if Chef::Config[:validation_key] && !File.exist?(File.(Chef::Config[:validation_key])) if winrm_auth_method == "plaintext" && config[:winrm_ssl] != true ui.error <<~EOM Validatorless bootstrap over unsecure winrm channels could expose your key to network sniffing. Please use a 'winrm_auth_method' other than 'plaintext', or enable ssl on #{server_name} then use the ---winrm-ssl flag to connect. EOM exit 1 end end true end |
#warn_on_short_session_timeout ⇒ Object
If session_timeout is too short, it is likely a holdover from “–winrm-session-timeout” which used minutes as its unit, instead of seconds. Warn the human so that they are not surprised.
878 879 880 881 882 883 884 885 |
# File 'lib/chef/knife/bootstrap.rb', line 878 def warn_on_short_session_timeout if session_timeout && session_timeout <= 15 ui.warn <<~EOM You provided '--session-timeout #{session_timeout}' second(s). Did you mean '--session-timeout #{session_timeout * 60}' seconds? EOM end end |
#winrm? ⇒ Boolean
933 934 935 |
# File 'lib/chef/knife/bootstrap.rb', line 933 def winrm? connection_protocol == "winrm" end |
#winrm_auth_method ⇒ Object
FIXME: someone needs to clean this up properly: github.com/chef/chef/issues/9645 This code is deliberately left without an abstraction around deprecating the config options to avoid knife plugins from using those methods (which will need to be deprecated and break them) via inheritance (ruby does not have a true ‘private` so the lack of any inheritable implementation is because of that).
754 755 756 |
# File 'lib/chef/knife/bootstrap.rb', line 754 def winrm_auth_method config.key?(:winrm_auth_method) ? config[:winrm_auth_method] : config.key?(:winrm_authentications_protocol) ? config[:winrm_authentication_protocol] : "negotiate" # rubocop:disable Style/NestedTernaryOperator end |
#winrm_opts ⇒ Object
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 |
# File 'lib/chef/knife/bootstrap.rb', line 1054 def winrm_opts return {} unless winrm? opts = { winrm_transport: winrm_auth_method, # winrm gem and train calls auth method 'transport' winrm_basic_auth_only: config[:winrm_basic_auth_only] || false, ssl: config[:winrm_ssl] === true, ssl_peer_fingerprint: config[:winrm_ssl_peer_fingerprint], } if winrm_auth_method == "kerberos" opts[:kerberos_service] = config[:kerberos_service] if config[:kerberos_service] opts[:kerberos_realm] = config[:kerberos_realm] if config[:kerberos_service] end if config[:ca_trust_file] opts[:ca_trust_path] = config[:ca_trust_file] end opts[:operation_timeout] = session_timeout opts end |
#winrm_warn_no_ssl_verification ⇒ Object
887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 |
# File 'lib/chef/knife/bootstrap.rb', line 887 def winrm_warn_no_ssl_verification return unless winrm? # REVIEWER NOTE # The original check from knife plugin did not include winrm_ssl_peer_fingerprint # Reference: # https://github.com/chef/knife-windows/blob/92d151298142be4a4750c5b54bb264f8d5b81b8a/lib/chef/knife/winrm_knife_base.rb#L271-L273 # TODO Seems like we should also do a similar warning if ssh_verify_host == false if config[:ca_trust_file].nil? && config[:winrm_no_verify_cert] && config[:winrm_ssl_peer_fingerprint].nil? ui.warn <<~WARN * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * SSL validation of HTTPS requests for the WinRM transport is disabled. HTTPS WinRM connections are still encrypted, but knife is not able to detect forged replies or spoofing attacks. To work around this issue you can use the flag `--winrm-no-verify-cert` or add an entry like this to your knife configuration file: # Verify all WinRM HTTPS connections knife[:winrm_no_verify_cert] = true You can also specify a ca_trust_file via --ca-trust-file, or the expected fingerprint of the target host's certificate via --winrm-ssl-peer-fingerprint. WARN end end |