Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make a macOS user capable of being the service's user-account #40

Draft
wants to merge 7 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 38 additions & 34 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

...
* Added: macOS installation now creates a user to run the service as, optionally as an administrator.

## 3.1.0 - 2020-02-26
## 3.1.1: 2020-07-31

* Fixed: systemd path to buildkite agent logs; needs to be a file, not a directory.

## 3.1.0: 2020-02-26

Add support for shims/scripts wrapping the `buildkite-agent` binary, instead of directly calling the binary.
This can be useful, for example, if you need to fetch your agent registration token from a secret store before running `buildkite-agent`.
Expand All @@ -18,66 +22,66 @@ If not using a shim/script, no changes are needed.
To use a shim instead of directly calling the `buildkite-agent` binary, point `buildkite_agent_executable`
to your shim, and `buildkite_agent_start_command` to your shim's args.

- Added `buildkite_agent_executable` option.
- Added `buildkite_agent_start_command` option.
* Added `buildkite_agent_executable` option.
* Added `buildkite_agent_start_command` option.

## 3.0.0 - 2019-11-13
## 3.0.0: 2019-11-13

Version bump for breaking change. This is the same as 2.2.1 with corrected meta/main.yml.

## 2.2.1 - 2019-11-12
## 2.2.1: 2019-11-12

- fixes to ansible 2.9.0 updates.
* fixes to ansible 2.9.0 updates.

## 2.2.0 - 2019-11-07
## 2.2.0: 2019-11-07

- porting to support ansible 2.9.0.
* porting to support ansible 2.9.0.

## 2.1.0 - 2019-08-20
## 2.1.0: 2019-08-20

- `buildkite_agent_username` option for configuring the name of the user to run the service as.
- `buildkite_agent_user_description` option for configuring the description of the user to run the service as.
* `buildkite_agent_username` option for configuring the name of the user to run the service as.
* `buildkite_agent_user_description` option for configuring the description of the user to run the service as.

## 2.0.0 - 2019-08-11
## 2.0.0: 2019-08-11

- require ansible `2.8.x` for `win_user_profile` support.
- take care of `win_nssm` deprecations within ansible 2.8.x.
* require ansible `2.8.x` for `win_user_profile` support.
* take care of `win_nssm` deprecations within ansible 2.8.x.

## 1.2.1 - 2019-04-25
## 1.2.1: 2019-04-25

- `buildkite_agent_nssm_exe` option.
- `buildkite_agent_tags_including_queue` option.
* `buildkite_agent_nssm_exe` option.
* `buildkite_agent_tags_including_queue` option.

## 1.2.0 - 2019-03-16
## 1.2.0: 2019-03-16

### Added

- `buildkite_agent_allow_service_startup` option.
- `buildkite_agent_expose_secrets` option.
- `buildkite_agent_tags_from_gcp_labels` option.
- `buildkite_agent_start_parameters` option for Debian and Windows.
- Debian `buildkite_agent_systemd_override_template` option.
- Related - stop using systemd _template_ unit file (because `buildkite_agent_start_parameters` and v3.6.0+ allow `--spawn` for multiple job-runners).
* `buildkite_agent_allow_service_startup` option.
* `buildkite_agent_expose_secrets` option.
* `buildkite_agent_tags_from_gcp_labels` option.
* `buildkite_agent_start_parameters` option for Debian and Windows.
* Debian `buildkite_agent_systemd_override_template` option.
* Related - stop using systemd _template_ unit file (because `buildkite_agent_start_parameters` and v3.6.0+ allow `--spawn` for multiple job-runners).

## 1.1.0 - 2018-12-11
## 1.1.0: 2018-12-11

### Added

- macOS support - #7
- Windows support - #6
* macOS support - #7
* Windows support - #6

## 1.0.0 - 2018-04-18
## 1.0.0: 2018-04-18

### Added

- Support for Buildkite Agent v3.
- Support for Ubuntu 16.04.
* Support for Buildkite Agent v3.
* Support for Ubuntu 16.04.

### Removed

- Remove support for Ubuntu 14.04.
- Remove support for Buildkite Agent v2.
* Remove support for Ubuntu 14.04.
* Remove support for Buildkite Agent v2.

## 0.1.0

- Initial release.
* Initial release.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Variable names below map to [the agent configuration documentation](https://buil
- `buildkite_agent_bootstrap_script`
- `buildkite_agent_git_clean_flags`
- `buildkite_agent_git_clone_flags`
- `buildkite_agent_grant_admin` - If `true` make the `buildkite_agent_username` user be a member of the local `Administrators` group. You must assess your own security risk tradeoff with the necessity for build tools needing privileges.
- `buildkite_agent_no_color`
- `buildkite_agent_no_command_eval`
- `buildkite_agent_no_plugins`
Expand Down Expand Up @@ -62,7 +63,6 @@ Variable names below map to [the agent configuration documentation](https://buil

- `buildkite_agent_nssm_exe` - the full path to nssm.exe in case it's not on `PATH`.
- `buildkite_agent_nssm_version` - Which version of [NSSM] to use to manage the buildkite-agent process as a service.
- `buildkite_agent_windows_grant_admin` - If `True` make the `buildkite_agent_username` user be a member of the local `Administrators` group. You must assess your own security risk tradeoff with the necessity for Windows build tools needing privileges.

#### Darwin

Expand Down
2 changes: 1 addition & 1 deletion defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ buildkite_agent_systemd_override_template: buildkite-agent.override.conf.j2
buildkite_agent_nssm_exe: "C:/ProgramData/chocolatey/bin/nssm.exe"
buildkite_agent_nssm_version: "2.24.101.20180116"
buildkite_agent_platform: "amd64"
buildkite_agent_windows_grant_admin: False
buildkite_agent_grant_admin: False

# Darwin options
buildkite_agent_load_bash_profile: yes
188 changes: 188 additions & 0 deletions files/create-local-user.Darwin.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
#!/usr/bin/env bash
set -o errexit -o pipefail -o nounset
[[ -n "${SCRIPT_DEBUG:-}" ]] && set -o xtrace

function log() {
echo "${@}" >&2
}

function die() {
log "${@}"
exit 1
}

# Print traceback of call stack, starting from the call location.
# An optional argument can specify how many additional stack frames to skip.
print_traceback() {
local skip=${1:-0}
local start=$((skip + 1))
local end=${#BASH_SOURCE[@]}
local curr=0
log "Traceback (most recent call first):"
for ((curr = start; curr < end; curr++)); do
local prev=$((curr - 1))
local func="${FUNCNAME[$curr]}"
local file="${BASH_SOURCE[$curr]}"
local line="${BASH_LINENO[$prev]}"
log " at ${file}:${line} in ${func}()"
done
}

_err_trap() {
local err=$?
local cmd="${BASH_COMMAND:-}"
# Disable echoing all commands as this makes the traceback really hard to follow
set +x
log "panic: uncaught error"
print_traceback 1
log "${cmd} exited ${err}"
}
trap _err_trap ERR

function array_contains() {
if [[ $1 =~ (^|[[:space:]])$2($|[[:space:]]) ]]; then
return 0
fi
return 1
}

function has_user() {
local user_name="${1?:"1st arg must be user name"}"
log "has_user: ${user_name}"
if /usr/bin/dscl . -list /Users | grep "^${user_name}$"; then
log "Exists"
return 0
fi
log "Does not exist"
return 1
}

function random_string() {
LC_CTYPE=C tr -dc 'a-zA-Z0-9' <"/dev/urandom" |
head -c 32 || log "random_string exit'd $?"
}

function find_new_user_id() {
local highest_id
highest_id="$(/usr/bin/dscl . -list /Users UniqueID | awk '{print $2}' | sort -ug | tail -n 1 | bc)"

echo $(( highest_id + 1 ))
}

function user_home_dir() {
local user_name="${1?:"1st arg must be user's username"}"

echo "/Users/${user_name}"
}

function create_user() {
local user_name="${1?:"1st arg must be user's username"}"
local user_pass="${2?:"2nd arg must be user's password"}"
local user_type="${3?:"3rd arg must be user's type [standard, admin]"}"
log "Creating ${user_name} ..."

local home_dir
home_dir="$(user_home_dir "${user_name}")"

if [[ -d "${home_dir}" ]]; then
echo "Error: ${home_dir} folder exists already."
return 1
fi

log "Finding new user ID..."
local user_uid
user_uid="$(find_new_user_id "${user_name}")"
log "Using user ID ${user_uid} ."

/usr/bin/dscl "." -create "/Users/${user_name}"
/usr/bin/dscl "." -create "/Users/${user_name}" RealName "${user_name}"
/usr/bin/dscl "." -create "/Users/${user_name}" UserShell "/bin/bash"
/usr/bin/dscl "." -create "/Users/${user_name}" UniqueID "${user_uid}"
/usr/bin/dscl "." -create "/Users/${user_name}" PrimaryGroupID "20" # '20' == 'staff'
/usr/bin/dscl "." -create "/Users/${user_name}" NFSHomeDirectory "${home_dir}"

/usr/bin/dscl "." -passwd "/Users/${user_name}" "${user_pass}"

# https://superuser.com/questions/20420/what-is-the-difference-between-the-default-groups-on-mac-os-x
/usr/bin/dscl "." -append /Groups/staff GroupMembership "${user_name}"
# https://stackoverflow.com/questions/1837889/authorize-a-non-admin-developer-in-xcode-mac-os
/usr/bin/dscl "." -append /Groups/_developer GroupMembership "${user_name}"
if [[ "${user_type}" == "admin" ]]; then
log "Making ${user_name} an admin..."
/usr/bin/dscl "." -append /Groups/admin GroupMembership "${user_name}"
fi

# create home directory
/bin/cp -R "/System/Library/User Template/English.lproj" "${home_dir}"
/usr/sbin/chown -R "${user_name}:staff" "${home_dir}"
}

function write_user_defaults() {
local user_name="${1?:"1st arg must be user's username"}"
log "Writing user defaults for ${user_name} ..."

local home_dir
home_dir="$(user_home_dir "${user_name}")"

# disable Apple ID, iCloud, Siri, etc
local PRODUCT_VERSION
PRODUCT_VERSION="$(sw_vers -productVersion)"
local PRODUCT_BUILD
PRODUCT_BUILD="$(/usr/bin/sw_vers -buildVersion)"
local SETUP_ASSISTANT
SETUP_ASSISTANT="${home_dir}/Library/Preferences/com.apple.SetupAssistant"
/usr/bin/defaults write "${SETUP_ASSISTANT}" DidSeeApplePaySetup -bool TRUE
/usr/bin/defaults write "${SETUP_ASSISTANT}" DidSeeAvatarSetup -bool TRUE
/usr/bin/defaults write "${SETUP_ASSISTANT}" DidSeeCloudDiagnostics -bool TRUE
/usr/bin/defaults write "${SETUP_ASSISTANT}" DidSeeCloudSetup -bool TRUE
/usr/bin/defaults write "${SETUP_ASSISTANT}" DidSeeSiriSetup -bool TRUE
/usr/bin/defaults write "${SETUP_ASSISTANT}" DidSeeSyncSetup -bool TRUE
/usr/bin/defaults write "${SETUP_ASSISTANT}" GestureMovieSeen none
/usr/bin/defaults write "${SETUP_ASSISTANT}" DidSeeSyncSetup2 -bool TRUE
/usr/bin/defaults write "${SETUP_ASSISTANT}" DidSeeTouchIDSetup -bool TRUE
/usr/bin/defaults write "${SETUP_ASSISTANT}" DidSeeiCloudLoginForStorageServices -bool TRUE
/usr/bin/defaults write "${SETUP_ASSISTANT}" LastSeenCloudProductVersion "${PRODUCT_VERSION}"
/usr/bin/defaults write "${SETUP_ASSISTANT}" LastSeenBuddyBuildVersion "${PRODUCT_BUILD}"
/usr/bin/defaults write "${SETUP_ASSISTANT}" DidSeePrivacy -bool TRUE
/usr/bin/defaults write "${SETUP_ASSISTANT}" DidSeeSiriSetup -bool TRUE
/usr/bin/defaults write "${SETUP_ASSISTANT}" DidSeeAppearanceSetup -bool TRUE
/usr/bin/defaults write "${SETUP_ASSISTANT}" DidSeeTrueTonePrivacy -bool TRUE

/usr/sbin/chown "${user_name}" "${SETUP_ASSISTANT}.plist"
}

function configure_auto_login() {
local user_name="${1?:"1st arg must be user's username"}"
local user_pass="${2?:"2nd arg must be user's password"}"

log "Setting up automatic login for ${user_name} ..."

local kc_password_path
kc_password_path="$(dirname "${BASH_SOURCE[0]}")/kcpassword"
"${kc_password_path}" "${user_pass}"
/bin/chmod 600 "/private/etc/kcpassword"
/usr/bin/defaults write /Library/Preferences/com.apple.loginwindow autoLoginUser "${user_name}"
/bin/chmod 644 "/Library/Preferences/com.apple.loginwindow.plist"
}

user_name="${1?:"1st arg must be user's name"}"
user_type="${2:-"standard"}"
auto_login="${3:-"true"}"
user_pass="${USER_PASSWORD:-"$(random_string)"}"

if has_user "${user_name}"; then
die "Exiting: User exists ${user_name}."
fi

home_dir="$(user_home_dir "${user_name}")"
if [[ -d "${home_dir}" ]]; then
die "Exiting: Home directory ${home_dir} already existed."
fi

create_user "${user_name}" "${user_pass}" "${user_type}"
write_user_defaults "${user_name}"
if [[ "${auto_login}" == "true" ]]; then
configure_auto_login "${user_name}" "${user_pass}"
else
log "Skip: auto_login was ${auto_login}. If you want the user to be automatically logged in, choose 'true'."
fi
39 changes: 39 additions & 0 deletions files/kcpassword
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env python

# https://github.com/xfreebird/kcpassword/blob/master/kcpassword

# Port of Gavin Brock's Perl kcpassword generator to Python, by Tom Taylor
# <[email protected]>.
# Perl version: http://www.brock-family.org/gavin/perl/kcpassword.html
# https://github.com/timsutton/osx-vm-templates/blob/master/scripts/support/set_kcpassword.py

import sys
import os

def kcpassword(passwd):
# The magic 11 bytes - these are just repeated
# 0x7D 0x89 0x52 0x23 0xD2 0xBC 0xDD 0xEA 0xA3 0xB9 0x1F
key = [125,137,82,35,210,188,221,234,163,185,31]
key_len = len(key)

passwd = [ord(x) for x in list(passwd)]
# pad passwd length out to an even multiple of key length
r = len(passwd) % key_len
if (r > 0):
passwd = passwd + [0] * (key_len - r)

for n in range(0, len(passwd), len(key)):
ki = 0
for j in range(n, min(n+len(key), len(passwd))):
passwd[j] = passwd[j] ^ key[ki]
ki += 1

passwd = [chr(x) for x in passwd]
return "".join(passwd)

if __name__ == "__main__":
passwd = kcpassword(sys.argv[1])
fd = os.open('/etc/kcpassword', os.O_WRONLY | os.O_CREAT, 0o600)
file = os.fdopen(fd, 'w')
file.write(passwd)
file.close()
Loading