From 343ed253e1d6b4eb8842ddc88ba4cd94d6792949 Mon Sep 17 00:00:00 2001 From: Chris Patuzzo Date: Sun, 10 Sep 2017 15:50:35 +0100 Subject: [PATCH] Run chef with additional permissions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to install homebrew, the script needs to be run as the current user, but it also requires sudo rights to change the ownership of /usr/local/bin amongst other things. Chef is being run in a subprocess and as a result, there’s no TTY associated with the shell. This means Homebrew can’t ask for a password when it runs sudo and the process fails. This workaround temporarily grants the user the right to run sudo commands without a password for the duration of the chef run. It revokes them at the end, even if chef fails. There’s some discussion about this here: https://github.com/chef-cookbooks/homebrew/issues/105 --- lib/zz/commands/provision.rb | 4 ++++ lib/zz/exec.rb | 14 +++++++++++++ lib/zz/path.rb | 8 ++++++++ spec/zz/commands/provision_spec.rb | 14 +++++++++++-- spec/zz/exec_spec.rb | 32 ++++++++++++++++++++++++++++++ 5 files changed, 70 insertions(+), 2 deletions(-) diff --git a/lib/zz/commands/provision.rb b/lib/zz/commands/provision.rb index 25a6408..3f3755b 100644 --- a/lib/zz/commands/provision.rb +++ b/lib/zz/commands/provision.rb @@ -3,7 +3,11 @@ module Provision class << self def execute(args) Exec.install_chef unless Exec.chef_installed? + + Exec.grant_permissions Exec.run_chef + ensure + Exec.revoke_permissions end def name diff --git a/lib/zz/exec.rb b/lib/zz/exec.rb index 68a085e..afaacab 100644 --- a/lib/zz/exec.rb +++ b/lib/zz/exec.rb @@ -20,6 +20,20 @@ def chef_installed? def run_chef execute("chef-solo --config #{Path.chef_config}") end + + def grant_permissions(u = ENV["USER"]) + execute( + "echo '#{u} ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/#{u}" + ) + end + + def revoke_permissions(u = ENV["USER"]) + execute("sudo rm -f /etc/sudoers.d/#{u}") + end + + def homebrew_installed? + execute("which brew") + end end end end diff --git a/lib/zz/path.rb b/lib/zz/path.rb index 3fca8e4..64595a6 100644 --- a/lib/zz/path.rb +++ b/lib/zz/path.rb @@ -32,6 +32,14 @@ def chef_config def chef_installer "https://www.opscode.com/chef/install.sh" end + + def homebrew_remote_installer + "https://raw.githubusercontent.com/Homebrew/install/master/install" + end + + def homebrew_local_installer + to("tmp/hombrew_installer") + end end end end diff --git a/spec/zz/commands/provision_spec.rb b/spec/zz/commands/provision_spec.rb index 4ae71c0..b2aa0cb 100644 --- a/spec/zz/commands/provision_spec.rb +++ b/spec/zz/commands/provision_spec.rb @@ -19,10 +19,20 @@ subject.execute([]) end - it "runs chef" do - expect(ZZ::Exec).to receive(:run_chef) + it "grants permissions, then runs chef, then revokes permissions" do + expect(ZZ::Exec).to receive(:grant_permissions).ordered + expect(ZZ::Exec).to receive(:run_chef).ordered + expect(ZZ::Exec).to receive(:revoke_permissions).ordered + subject.execute([]) end + it "ensures permissions are revoked if something goes wrong" do + allow(ZZ::Exec).to receive(:run_chef).and_raise("test") + expect(ZZ::Exec).to receive(:revoke_permissions) + + expect { subject.execute([]) }.to raise_error("test") + end + pending "--list option the prints the run_list" end diff --git a/spec/zz/exec_spec.rb b/spec/zz/exec_spec.rb index 0cb9143..054ca4a 100644 --- a/spec/zz/exec_spec.rb +++ b/spec/zz/exec_spec.rb @@ -53,4 +53,36 @@ subject.run_chef end end + + describe ".grant_permissions" do + it "grants the permission to run sudo without a password" do + expect(subject).to receive(:execute) + .with("echo 'foo ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/foo") + + subject.grant_permissions("foo") + end + + it "defaults to the current user" do + allow(ENV).to receive(:[]).and_return("bar") + + expect(subject).to receive(:execute) + .with("echo 'bar ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/bar") + + subject.grant_permissions + end + end + + describe ".revoke_permissions" do + it "revokes the permission to run sudo without a password" do + expect(subject).to receive(:execute).with("sudo rm -f /etc/sudoers.d/foo") + subject.revoke_permissions("foo") + end + + it "defaults to the current user" do + allow(ENV).to receive(:[]).and_return("bar") + + expect(subject).to receive(:execute).with("sudo rm -f /etc/sudoers.d/bar") + subject.revoke_permissions + end + end end