Skip to content

Commit

Permalink
Merge pull request #21 from emalloy/em/124512076
Browse files Browse the repository at this point in the history
add stdlib::setup_sudoers
  • Loading branch information
aaron-lane authored Mar 15, 2019
2 parents 5717115 + 5b39b31 commit 321f5de
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 4 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ ruby '2.5.3'

source 'https://rubygems.org/' do
gem 'kitchen-terraform', '~> 4.1.0'
gem 'retriable', '~> 3.1.2'
end
4 changes: 2 additions & 2 deletions examples/simple_example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ $ curl -H Metadata-Flavor:Google http://metadata.google.internal/computeMetadata
| Name | Description |
|------|-------------|
| nat_ip | Public IP address of the example compute instance. |
| project_id | |
| region | |
| project_id | The project id used when managing resources. |
| region | The region used when managing resources. |

[^]: (autogen_docs_end)

Expand Down
13 changes: 12 additions & 1 deletion examples/simple_example/files/startup-script-custom
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
#! /bin/bash

# add example_user for test
sudo useradd -m example_user1

# add another user for inverse test - \
# sudo should not work for this user
sudo useradd -m example_user2
sudo sh -c 'echo testpassword2019 | passwd --stdin example_user2'

stdlib::info "Checking whether to add any users to sudoers ..."
stdlib::setup_sudoers

URL=${URL:-"http://ifconfig.co/json"}
stdlib::info "Fetching ${URL}"
stdlib::cmd curl --silent "${URL}"
echo
9 changes: 9 additions & 0 deletions examples/simple_example/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,20 @@ provider "google" {

module "startup-scripts" {
source = "../../"
enable_setup_sudoers = true
}

data "google_compute_image" "os" {
project = "centos-cloud"
family = "centos-7"
}

resource "google_compute_project_metadata" "example" {
metadata = {
sudoers = "example_user1,example_user2"
}
}

resource "google_compute_instance" "example" {
name = "startup-scripts-example1"
description = "Startup Scripts Example"
Expand All @@ -49,6 +56,7 @@ resource "google_compute_instance" "example" {

boot_disk {
auto_delete = true

initialize_params {
image = "${data.google_compute_image.os.self_link}"
type = "pd-standard"
Expand All @@ -57,6 +65,7 @@ resource "google_compute_instance" "example" {

network_interface {
network = "default"

access_config {
// Ephemeral IP
}
Expand Down
53 changes: 53 additions & 0 deletions files/setup_sudoers.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#! /bin/bash
# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Read the project metadata key named "sudoers" and add each comma separated value to
# the sudoers file.
stdlib::setup_sudoers() {
local user user_list sudoers_file
user_list="$(stdlib::metadata_get -k 'project/attributes/sudoers')"

if [[ -z "${user_list}" ]]; then
stdlib::debug "Skipping sudoers setup. \
The value of the project metadata key named sudoers is empty. \
Set sudoers to a comma separated list to enable sudo \
support, e.g. sudoers=jmccune,pames"
return 0
fi

sudoers_file="/etc/sudoers"
sudoers_d="/etc/sudoers.d"

for user in ${user_list//,/ }; do
if [[ -f "${sudoers_d}/${user}" ]] \
&& ( grep -q "^${user}"'\b' "${sudoers_d}/${user}" ) \
|| (grep -q "^${user}"'\b' "${sudoers_file}") ; then
stdlib::debug "User ${user} is already in /etc/sudoers or \
/etc/sudoers.d/${user}, taking no action"
else
stdlib::info "Adding ${user} to /etc/sudoers.d/${user}"
echo -e "${user}\tALL= (ALL)\tNOPASSWD: ALL" \
> "${sudoers_d}/${user}" \
&& chmod 0440 "${sudoers_d}/${user}"
fi
done

if visudo -c ; then
stdlib::info "sudoers config valid!"
else
stdlib::error "sudoers config invalid!"
return 1
fi
}
4 changes: 4 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,21 @@
locals {
stdlib_head = "${file("${path.module}/files/startup-script-stdlib-head.sh")}"
gsutil_el = "${var.enable_init_gsutil_crcmod_el ? file("${path.module}/files/init_gsutil_crcmod_el.sh") : ""}"
sudoers = "${var.enable_setup_sudoers ? file("${path.module}/files/setup_sudoers.sh") : ""}"
get_from_bucket = "${var.enable_get_from_bucket ? file("${path.module}/files/get_from_bucket.sh") : ""}"
setup_init_script = "${(var.enable_setup_init_script && var.enable_get_from_bucket) ? file("${path.module}/files/setup_init_script.sh") : ""}"
stdlib_body = "${file("${path.module}/files/startup-script-stdlib-body.sh")}"

# List representing complete content, to be concatenated together.
stdlib_list = [
"${local.stdlib_head}",
"${local.gsutil_el}",
"${local.get_from_bucket}",
"${local.setup_init_script}",
"${local.sudoers}",
"${local.stdlib_body}",
]

# Final content output to the user
stdlib = "${join("", local.stdlib_list)}"
}
2 changes: 1 addition & 1 deletion test/fixtures/simulated_ci_environment/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,6 @@ For example:

| Name | Description |
|------|-------------|
| phoogle_sa | The SA KEY JSON content. Store in GOOGLE_CREDENTIALS. |
| service_account_private_key | The SA KEY JSON content. Store in GOOGLE_CREDENTIALS. This is equivalent to the `phoogle_sa` output in the infra repository |

[^]: (autogen_docs_end)
41 changes: 41 additions & 0 deletions test/integration/simple_example/controls/sudoers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

require 'retriable'

control 'simple startup-script-custom' do
title "With the simple example of startup-script-custom calling stdlib::info and stdlib::cmd"

describe "gcloud ... get-serial-port-output startup-scripts-example1" do
# Avoid racing against the instance boot sequence
before :all do
Retriable.retriable(tries: 20) do
get_serial_port_output = "gcloud compute instances get-serial-port-output startup-scripts-example1"
@cmd = command("#{get_serial_port_output} --project #{attribute('project_id')} --zone #{attribute('region')}-a")
if not %r{systemd: Startup finished}.match(@cmd.stdout)
raise StandardError, "Not found: 'systemd: Startup finished' in console output, cannot proceed"
end
end
end

subject do
@cmd
end

its('exit_status') { should be 0 }
its('stdout') { should match(%r{Info \[\d+\]: Adding example_user1 to /etc/sudoers}) }
its('stdout') { should match(%r{Info \[\d+\]: sudoers config valid!}) }
its('stdout') { should match('INFO startup-script: Return code 0.') }
end
end
5 changes: 5 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,8 @@ variable "enable_setup_init_script" {
description = "If not false, include stdlib::setup_init_script() prior to executing startup-script-custom. Call this function to load an init script from GCS into /etc/init.d and initialize it with chkconfig. This function depends on stdlib::get_from_bucket, so this function won't be enabled if enable_get_from_bucket is false."
default = "false"
}

variable "enable_setup_sudoers" {
description = "If true, include stdlib::setup_sudoers() prior to executing startup-script-custom. Call this function from startup-script-custom to setup unix usernames in sudoers Comma separated values must be posted to the project metadata key project/attributes/sudoers"
default = "false"
}

0 comments on commit 321f5de

Please sign in to comment.