Merge branch 'dev/jweyer/remote_management_updates' into release/6.0.0
This commit is contained in:
Коммит
f022984b98
|
@ -5,6 +5,15 @@
|
|||
### Removed
|
||||
- Removed the `plist` resource
|
||||
|
||||
### Fixed
|
||||
- Fixed issues with ARD not working on macOS Monterey.
|
||||
|
||||
### Added
|
||||
- Added additional functionality to the [remote_management](resources/remote_management.rb) resource.
|
||||
- You can now specify the users to whose privileges will be configured.
|
||||
- You can now specify the privileges to bestow upon the given users.
|
||||
- You can now set the computer info fields; this is helpful for stratifying computers within ARD.
|
||||
|
||||
## [5.0.4] - 2023-01-31
|
||||
|
||||
### Added
|
||||
|
|
|
@ -1,50 +1,63 @@
|
|||
remote_management
|
||||
===
|
||||
|
||||
Use the **remote_management** resource to manage the "Remote Management" settings, found in System
|
||||
Preferences > Sharing > Remote Management. Under the hood, the [**remote_management**](https://github.com/Microsoft/macos-cookbook/blob/master/resources/remote_management.rb) resource
|
||||
executes the `kickstart` command, located in ARDAgent.app (one of macOS' "core services").
|
||||
Use the **remote_management** resource to manage the "Remote Management" settings in System Preferences > Sharing > Remote Management. Under the hood, the [remote_management](../resources/remote_management.rb) resource utilizes the [kickstart](https://ss64.com/osx/kickstart.html) script.
|
||||
|
||||
Syntax
|
||||
------
|
||||
|
||||
The **remote_management** resource block declares a basic description of the command configuration
|
||||
and an action executed. For example:
|
||||
|
||||
```ruby
|
||||
remote_management 'enable remote management' do
|
||||
action :enable
|
||||
remote_management 'configure remote management' do
|
||||
users [String, Array]
|
||||
privileges [String, Array]
|
||||
computer_info [String, Array]
|
||||
end
|
||||
```
|
||||
|
||||
where
|
||||
Properties
|
||||
-------
|
||||
|
||||
- `:enable` activates remote management and configures full privileges for all users on the system.
|
||||
- `:disable` deactivates the remote management agent and prevents it from activating at boot time.
|
||||
|
||||
The default `:enable` action is equivalent to configuring the following
|
||||
**System Preferences > Sharing** settings:
|
||||
|
||||
![Sharing Preferences](sharing_preferences.png)
|
||||
|
||||
The full syntax for all of the properties that are available to the **remote_management**
|
||||
resource is:
|
||||
|
||||
```ruby
|
||||
remote_management 'description' do
|
||||
action Symbol # defaults to [:enable] if not specified
|
||||
end
|
||||
```
|
||||
* `users`
|
||||
* **Description:** the user(s) whoose ARD privileges will be configured.
|
||||
* **Usage:** a single user can be specified in the form of a string, or multiple users can be specified as an array of strings. Specifying 'all' is a special case; all local users will be configured.
|
||||
* **Default:** `'all'`
|
||||
* Privileges will be configured for all local users.
|
||||
* **Constraints:** specified users must exist on the system.
|
||||
<br></br>
|
||||
|
||||
* `privileges`
|
||||
* **Description:** the desired privileges to bestow upon the given user(s).
|
||||
**Usage:** a single privilege can be specified in the form of a string, or multiple privileges can be specified as an array of strings.
|
||||
* **Default:** `'all'`
|
||||
* **Constraints:** the list of optional privileges bellow
|
||||
* `all` → grant all privileges (default)
|
||||
* `none` → disable all privileges for the specified user
|
||||
* `DeleteFiles` → delete files
|
||||
* `TextMessages` → send a text message
|
||||
* `OpenQuitApp` → open and quit applications
|
||||
* `GenerateReport` → generate reports
|
||||
* `RestartShutDown` → restart *and/or* shutdown
|
||||
* `SendFile` → send *and/or* retrieve files
|
||||
* `ChangeSetting` → change system settings
|
||||
* `ShowObserve` → show the client when being observed or controlled
|
||||
* `ControlObserve` → control AND observe (unless ObserveOnly is also specified)
|
||||
* `ObserveOnly` → modify ControlObserve option to allow Observe mode only
|
||||
<br></br>
|
||||
|
||||
* `computer_info`
|
||||
* **Description:** Info fields; helpful for stratifying computers in the ARD client app.
|
||||
* **Usage** a single info field can be added as a string, or multiple info fields can be added as an array of strings.
|
||||
* **Default:** `[]`
|
||||
* No info fields will be added.
|
||||
* **Constraints:** there is a maximum of four info fields allowed.
|
||||
|
||||
Actions
|
||||
-------
|
||||
|
||||
This resource has the following actions:
|
||||
* `enable`
|
||||
* **Description:** activate remote management and configure full privileges for all users on the system.
|
||||
* **GUI Equivalent:** activating with the privileges property set to 'all' is equivalent to the following:
|
||||
![Sharing Preferences](sharing_preferences.png)
|
||||
|
||||
`:enable`
|
||||
|
||||
Activate remote management and configure full privileges for all users on the system.
|
||||
|
||||
`:disable`
|
||||
|
||||
Deactivate the remote management agent and prevent it from activating at boot time.
|
||||
* `disable`
|
||||
* **Description:** deactivate the remote management agent and prevent it from activating at boot time.
|
||||
|
|
|
@ -1,35 +1,361 @@
|
|||
include Chef::Mixin::ShellOut
|
||||
|
||||
module MacOS
|
||||
class RemoteManagement
|
||||
class << self
|
||||
def current_mask(users)
|
||||
using_global_privileges? ? Privileges::Value.new(value: global_settings_privilege_value).to_mask : current_user_masks(users).first
|
||||
end
|
||||
|
||||
def current_user_masks(users)
|
||||
users = all_local_users if users.include?('all')
|
||||
users.flatten.map { |user| Privileges::Mask.new(mask: individual_settings.fetch(user)) }
|
||||
end
|
||||
|
||||
def current_users_have_identical_masks?(users)
|
||||
return true if using_global_privileges?
|
||||
current_user_masks(users).map(&:to_i).uniq.one?
|
||||
end
|
||||
|
||||
def current_users_configured?(users)
|
||||
return true if using_global_privileges?
|
||||
users = all_local_users if users.include?('all')
|
||||
(users - individual_settings.keys).empty?
|
||||
end
|
||||
|
||||
def current_computer_info
|
||||
computer_info_hash.select { |k, _v| k.match?(Regexp.new('Text')) }.values.reject(&:empty?)
|
||||
end
|
||||
|
||||
def activated?
|
||||
::File.exist? '/Library/Application Support/Apple/Remote Desktop/RemoteManagement.launchd'
|
||||
if Chef::Version.new(Chef.node['platform_version']) >= Chef::Version.new('12.0.0')
|
||||
agent_running? && TCC::State.enabled?
|
||||
else
|
||||
agent_running?
|
||||
end
|
||||
end
|
||||
|
||||
def configured_for_all_users_and_privileges?
|
||||
RemoteManagement.plist_content.include?('ARD_AllLocalUsers = true') &&
|
||||
RemoteManagement.plist_content.include?(full_privileges)
|
||||
def kickstart
|
||||
'/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart'
|
||||
end
|
||||
|
||||
def plist_content
|
||||
shell_out('/usr/libexec/PlistBuddy -c Print /Library/Preferences/com.apple.RemoteManagement.plist').stdout
|
||||
private
|
||||
|
||||
def agent_running?
|
||||
::File.exist?('/Library/Application Support/Apple/Remote Desktop/RemoteManagement.launchd')
|
||||
end
|
||||
|
||||
def full_privileges
|
||||
text_messages = 1 << 0
|
||||
control_observe = 1 << 1
|
||||
send_files = 1 << 2
|
||||
delete_files = 1 << 3
|
||||
generate_reports = 1 << 4
|
||||
open_quit_apps = 1 << 5
|
||||
change_settings = 1 << 6
|
||||
restart_shutdown = 1 << 7
|
||||
show_observe = 1 << 30
|
||||
def using_global_privileges?
|
||||
using_global_settings? && !global_settings_privilege_value.nil?
|
||||
end
|
||||
|
||||
(text_messages | control_observe | send_files |
|
||||
delete_files | generate_reports | open_quit_apps |
|
||||
change_settings | restart_shutdown | show_observe).to_s
|
||||
def using_global_settings?
|
||||
global_settings['ARD_AllLocalUsers'] || false
|
||||
end
|
||||
|
||||
def global_settings_privilege_value
|
||||
global_settings['ARD_AllLocalUsersPrivs']
|
||||
end
|
||||
|
||||
def global_settings
|
||||
::Plist.parse_xml(global_settings_xml) || {}
|
||||
end
|
||||
|
||||
def global_settings_xml
|
||||
shell_out!('/usr/bin/plutil -convert xml1 /Library/Preferences/com.apple.RemoteManagement.plist -o -').stdout
|
||||
rescue Mixlib::ShellOut::ShellCommandFailed => e
|
||||
e.message.match?(Regexp.new('file does not exist')) ? '' : raise
|
||||
end
|
||||
|
||||
def individual_settings
|
||||
dscl_naprivs.scan(/(?<user>^\S+)\s+(?<mask>-\d+)/).to_h.transform_values(&:to_i)
|
||||
end
|
||||
|
||||
def dscl_naprivs
|
||||
shell_out!('/usr/bin/dscl . -list /Users dsAttrTypeNative:naprivs').stdout
|
||||
end
|
||||
|
||||
def computer_info_xml
|
||||
shell_out!('/usr/bin/plutil -convert xml1 /Library/Preferences/com.apple.RemoteDesktop.plist -o -').stdout
|
||||
rescue Mixlib::ShellOut::ShellCommandFailed => e
|
||||
e.message.match?(Regexp.new('file does not exist')) ? '' : raise
|
||||
end
|
||||
|
||||
def computer_info_hash
|
||||
::Plist.parse_xml(computer_info_xml) || {}
|
||||
end
|
||||
|
||||
def all_local_users
|
||||
system_users = %w(root nobody daemon unknown smmsp www mysql sshd lp sendmail postfix eppc qtss cyrus mailman)
|
||||
(all_users.reject { |user| user.match?(/^_\w+$/) } - system_users)
|
||||
end
|
||||
|
||||
def all_users
|
||||
shell_out!('/usr/bin/dscl . -list /Users').stdout.split
|
||||
end
|
||||
end
|
||||
|
||||
module TCC
|
||||
module State
|
||||
class << self
|
||||
def enabled?
|
||||
post_event_service_enabled? && screencapture_service_enabled?
|
||||
end
|
||||
|
||||
def post_event_service_enabled?
|
||||
hash['postEvent'] || false
|
||||
end
|
||||
|
||||
def screencapture_service_enabled?
|
||||
hash['screenCapture'] || false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def hash
|
||||
::Plist.parse_xml(xml) || {}
|
||||
end
|
||||
|
||||
def xml
|
||||
shell_out!('sudo', '/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Support/tccstate').stdout
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module DB
|
||||
class << self
|
||||
def correct_privileges?
|
||||
screensharing_client_authorized_for_post_event_service? && screensharing_client_authorized_for_screencapture_service?
|
||||
end
|
||||
|
||||
def screensharing_client_authorized_for_post_event_service?
|
||||
screensharing_client_auth_value_for_post_event_service == 2
|
||||
end
|
||||
|
||||
def screensharing_client_authorized_for_screencapture_service?
|
||||
screensharing_client_auth_value_for_screencapture_service == 2
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def path
|
||||
'/Library/Application Support/com.apple.TCC/TCC.db'
|
||||
end
|
||||
|
||||
def screensharing_client_auth_value_for_post_event_service
|
||||
shell_out!('/usr/bin/sqlite3', path, "SELECT auth_value FROM access WHERE service='kTCCServicePostEvent' AND client='com.apple.screensharing.agent';").stdout.chomp.to_i
|
||||
end
|
||||
|
||||
def screensharing_client_auth_value_for_screencapture_service
|
||||
shell_out!('/usr/bin/sqlite3', path, "SELECT auth_value FROM access WHERE service='kTCCServiceScreenCapture' AND client='com.apple.screensharing.agent';").stdout.chomp.to_i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module SIP
|
||||
class << self
|
||||
def disabled?
|
||||
system_profiler_software_info.match?(Regexp.new('System Integrity Protection: Disabled'))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def system_profiler_software_info
|
||||
shell_out!('/usr/sbin/system_profiler', 'SPSoftwareDataType').stdout
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Privileges
|
||||
class << self
|
||||
def valid?(*privileges)
|
||||
list_invalid([privileges].flatten).empty?
|
||||
end
|
||||
|
||||
def validate!(*privileges)
|
||||
raise(Exceptions::Privileges::ValidationError, list_invalid(privileges)) unless valid?(privileges)
|
||||
rescue Exceptions::Privileges::ValidationError => e # raise property validation error if called from property coercion block
|
||||
called_by_chef_property_coerce? ? raise(Chef::Exceptions::ValidationFailed, e.message) : raise
|
||||
end
|
||||
|
||||
def format(*privileges)
|
||||
[privileges].flatten.map do |privilege|
|
||||
privilege.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
||||
.gsub(/([a-z])([A-Z])/, '\1_\2')
|
||||
.gsub(/(\s|-|_{2,})/, '_')
|
||||
.upcase.to_sym
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def called_by_chef_property_coerce?
|
||||
caller_locations.any? { |backtrace_location| ::File.basename(backtrace_location.path, '.rb') == 'property' && backtrace_location.label == 'coerce' }
|
||||
end
|
||||
|
||||
def list_invalid(privileges)
|
||||
privileges - BitMask.constants(false)
|
||||
end
|
||||
end
|
||||
|
||||
class Value
|
||||
extend Forwardable
|
||||
def_delegators :@value, :to_i, :zero?, :==, :+, :-, :|, :&, :^
|
||||
|
||||
def initialize(value: nil, privileges: nil)
|
||||
if value
|
||||
raise(Exceptions::Privileges::Value::ValidationError, value) unless valid?(value)
|
||||
@value = value
|
||||
elsif privileges
|
||||
privileges = Privileges.format(privileges)
|
||||
Privileges.validate!(privileges)
|
||||
@value = from_privileges(privileges)
|
||||
else
|
||||
raise(Exceptions::Privileges::Value::InvalidArgument, value, privileges)
|
||||
end
|
||||
end
|
||||
|
||||
def valid?(value)
|
||||
return true if value == BitMask::NONE
|
||||
(~(BitMask::ALL | BitMask::OBSERVE_ONLY) & value).zero?
|
||||
end
|
||||
|
||||
def from_privileges(privileges)
|
||||
return BitMask::ALL if privileges.include?(:ALL)
|
||||
return 0 if privileges.include?(:NONE)
|
||||
privileges.map { |priv| BitMask.const_get(priv.upcase) }.reduce(&:|)
|
||||
end
|
||||
|
||||
def to_mask
|
||||
Mask.new(mask: (@value - BitMask::NONE))
|
||||
end
|
||||
|
||||
def to_a
|
||||
return ['none'] if @value.zero?
|
||||
return ['all'] if @value == BitMask::ALL
|
||||
unformated_priv_strings = BitMask.constants(false).reject { |const| (BitMask.const_get(const) & @value).zero? || const == :ALL }.map(&:to_s).map(&:downcase)
|
||||
unformated_priv_strings.map { |priv| priv.to_s.split('_').map(&:capitalize).join }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Mask
|
||||
extend Forwardable
|
||||
def_delegators :@mask, :to_i, :zero?, :==, :+, :-, :|, :&, :^
|
||||
|
||||
def initialize(mask: nil, privileges: nil)
|
||||
if mask
|
||||
raise(Exceptions::Privileges::Value::ValidationError, mask) unless valid?(mask)
|
||||
@mask = mask
|
||||
elsif privileges
|
||||
privileges = Privileges.format(privileges)
|
||||
Privileges.validate!(privileges)
|
||||
@mask = from_privileges(privileges)
|
||||
else
|
||||
raise(Exceptions::Privileges::Value::InvalidArgument, mask, privileges)
|
||||
end
|
||||
end
|
||||
|
||||
def valid?(mask)
|
||||
return true if mask.zero?
|
||||
(~(BitMask::ALL | BitMask::OBSERVE_ONLY) & (mask + BitMask::NONE)).zero?
|
||||
end
|
||||
|
||||
def from_privileges(privileges)
|
||||
return -BitMask::NONE if privileges.include?(:NONE)
|
||||
return (BitMask::ALL - BitMask::NONE) if privileges.include?(:ALL)
|
||||
(privileges.map { |priv| BitMask.const_get(priv.upcase) }.reduce(&:|) - BitMask::NONE)
|
||||
end
|
||||
|
||||
def to_value
|
||||
Value.new(value: (@mask + BitMask::NONE))
|
||||
end
|
||||
|
||||
def to_a
|
||||
return ['none'] if (@mask + BitMask::NONE).zero?
|
||||
return ['all'] if @mask + BitMask::NONE == BitMask::ALL
|
||||
unformated_priv_strings = BitMask.constants(false).reject { |const| (BitMask.const_get(const) & (@mask + BitMask::NONE)).zero? || const == :ALL }
|
||||
unformated_priv_strings.map { |priv| priv.to_s.split('_').map(&:capitalize).join }
|
||||
end
|
||||
|
||||
# For Chef logging
|
||||
# Chef logs without this overide -> 'set privileges to MacOS::RemoteManagement::Privileges::Mask:0x00007fe032914c08 @mask=-2147483641> (was #<MacOS::RemoteManagement::Privileges::Mask:0x00007fe0329d7f78 @mask=-2147483645>)'
|
||||
# Chef logs with this override -> 'set privileges to ["TextMessages", "ControlObserve"] (was ["TextMessages", "ControlObserve", "SendFiles"])'
|
||||
def inspect
|
||||
to_a.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module BitMask
|
||||
TEXT_MESSAGES = 0b00000000000000000000000000000001
|
||||
CONTROL_OBSERVE = 0b00000000000000000000000000000010
|
||||
SEND_FILES = 0b00000000000000000000000000000100
|
||||
DELETE_FILES = 0b00000000000000000000000000001000
|
||||
GENERATE_REPORTS = 0b00000000000000000000000000010000
|
||||
OPEN_QUIT_APPS = 0b00000000000000000000000000100000
|
||||
CHANGE_SETTINGS = 0b00000000000000000000000001000000
|
||||
RESTART_SHUT_DOWN = 0b00000000000000000000000010000000
|
||||
OBSERVE_ONLY = 0b00000000000000000000000100000000
|
||||
SHOW_OBSERVE = 0b01000000000000000000000000000000
|
||||
ALL = 0b01000000000000000000000011111111
|
||||
NONE = 0b10000000000000000000000000000000
|
||||
end
|
||||
|
||||
module Exceptions
|
||||
class TCCError < RuntimeError
|
||||
def initialize
|
||||
super(message)
|
||||
end
|
||||
|
||||
def message
|
||||
String.new.tap do |message|
|
||||
message << "TCC does not have the correct authorizations for ARD to work!\n"
|
||||
message << "\t* Screensharing client not authorized for post event service\n" unless RemoteManagement::TCC::DB.screensharing_client_authorized_for_post_event_service?
|
||||
message << "\t* Screensharing client not authorized for screencapture service\n" unless RemoteManagement::TCC::DB.screensharing_client_authorized_for_screencapture_service?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class SIPEnabled < RuntimeError
|
||||
def initialize
|
||||
super('SIP is enabled! Please disable before using this resource')
|
||||
end
|
||||
end
|
||||
|
||||
module Privileges
|
||||
class ValidationError < ArgumentError
|
||||
def initialize(invalid_privileges)
|
||||
super("#{invalid_privileges.map(&:to_s).map(&:downcase)} are invalid privilege(s)! Valid privileges include: #{RemoteManagement::BitMask.constants(false).map(&:downcase).map(&:to_s)}")
|
||||
end
|
||||
end
|
||||
|
||||
module Value
|
||||
class ValidationError < ArgumentError
|
||||
def initialize(value)
|
||||
super("#{value.inspect} is an invalid value!")
|
||||
end
|
||||
end
|
||||
|
||||
class InvalidArgument < ArgumentError
|
||||
def initialize(*args)
|
||||
super("wrong number of arguments (given #{args.compact.size} expected 1)")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Mask
|
||||
class ValidationError < ArgumentError
|
||||
def initialize(mask)
|
||||
super("#{mask.inspect} is an invalid mask!")
|
||||
end
|
||||
|
||||
class InvalidArgument < ArgumentError
|
||||
def initialize(*args)
|
||||
super("wrong number of arguments (given #{args.compact.size} expected 1)")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,24 +3,85 @@ unified_mode true
|
|||
provides :remote_management
|
||||
default_action :enable
|
||||
|
||||
kickstart = '/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart'
|
||||
property :users,
|
||||
[String, Array],
|
||||
default: 'all',
|
||||
description: 'The user(s) whose ARD privileges will be configured.',
|
||||
coerce: ->(p) { [p].flatten }
|
||||
|
||||
property :privileges,
|
||||
[String, Array, RemoteManagement::Privileges::Mask],
|
||||
default: 'all',
|
||||
description: 'The desired privileges to bestow upon the given users.',
|
||||
coerce: ->(p) { p.is_a?(RemoteManagement::Privileges::Mask) ? p : RemoteManagement::Privileges::Mask.new(privileges: p) }
|
||||
|
||||
property :computer_info,
|
||||
[String, Array],
|
||||
default: [],
|
||||
description: 'Info fields; helpful for stratifying computers in ARD client app.',
|
||||
coerce: ->(p) { p.is_a?(Array) ? p.compact.map(&:to_s) : [p] },
|
||||
callbacks: { 'has too many elements; computer info excepts up to four info fields' => ->(p) { (p.is_a?(Array) && p.size < 4) } }
|
||||
|
||||
load_current_value do |desired|
|
||||
current_value_does_not_exist! unless RemoteManagement.activated? && RemoteManagement.current_users_configured?(desired.users) && RemoteManagement.current_users_have_identical_masks?(desired.users)
|
||||
privileges RemoteManagement.current_mask(desired.users)
|
||||
computer_info RemoteManagement.current_computer_info
|
||||
end
|
||||
|
||||
# TODO; the enable action should be decoupled from configuration; configure action should be added; default action should be [:configure, :enable]
|
||||
|
||||
action :enable do
|
||||
execute "#{kickstart} -activate" do
|
||||
not_if { RemoteManagement.activated? }
|
||||
end
|
||||
converge_if_changed(:privileges, :computer_info) do
|
||||
raise(RemoteManagement::Exceptions::SIPEnabled) unless RemoteManagement::TCC::SIP.disabled?
|
||||
raise(RemoteManagement::Exceptions::TCCError) unless RemoteManagement::TCC::DB.correct_privileges?
|
||||
|
||||
execute "#{kickstart} -configure -allowAccessFor -allUsers -access -on -privs -all" do
|
||||
not_if { RemoteManagement.configured_for_all_users_and_privileges? }
|
||||
execute 'restart the TCC daemon' do
|
||||
command 'sudo pkill -9 tccd'
|
||||
only_if { platform_version >= Chef::Version.new('12.0.0') }
|
||||
not_if { RemoteManagement::TCC::State.enabled? }
|
||||
end
|
||||
|
||||
converge_if_changed(:privileges) do
|
||||
if new_resource.users.include?('all')
|
||||
converge_by('setting privileges for all users') do
|
||||
privs_array = new_resource.privileges.to_a.map { |priv| priv.prepend('-') }
|
||||
|
||||
execute 'set privileges for all users' do
|
||||
command [RemoteManagement.kickstart, '-configure', '-allowAccessFor', '-allUsers', '-access', '-on', '-privs', privs_array].flatten
|
||||
end
|
||||
end
|
||||
else
|
||||
converge_by("setting privileges for #{new_resource.users.join(', ')}") do
|
||||
privs_array = new_resource.privileges.to_a.map { |priv| priv.prepend('-') }
|
||||
|
||||
execute 'set up Remote Management to only grant access to users with privileges' do
|
||||
command [RemoteManagement.kickstart, '-configure', '-allowAccessFor', '-specifiedUsers']
|
||||
end
|
||||
|
||||
execute "set privileges for #{new_resource.users.join(', ')}" do
|
||||
command [RemoteManagement.kickstart, '-configure', '-access', '-on', '-privs', privs_array, '-users', new_resource.users].flatten
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
converge_if_changed(:computer_info) do
|
||||
new_resource.computer_info.each_with_index do |info, i|
|
||||
execute "set computer info field #{i + 1}" do
|
||||
command [RemoteManagement.kickstart, '-configure', '-computerinfo', "-set#{i + 1}", "-#{i + 1}", info]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
execute 'activate the Remote Management service and restart the agent' do
|
||||
command [RemoteManagement.kickstart, '-activate', '-restart', '-agent']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
action :disable do
|
||||
execute "#{kickstart} -deactivate" do
|
||||
only_if { RemoteManagement.activated? }
|
||||
end
|
||||
|
||||
execute "#{kickstart} -stop" do
|
||||
execute 'stop the Remote Management service and deactivate it so it will not start after the next restart' do
|
||||
command [RemoteManagement.kickstart, '-deactivate', '-stop']
|
||||
only_if { RemoteManagement.activated? }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
unified_mode true
|
||||
|
||||
provides :remote_management
|
||||
default_action :enable
|
||||
|
||||
property :users,
|
||||
[String, Array],
|
||||
default: 'all',
|
||||
description: 'The user(s) whoose ARD privileges will be configured.',
|
||||
coerce: ->(p) { [p].flatten }
|
||||
|
||||
property :privileges,
|
||||
[String, Array, Integer],
|
||||
default: 'all',
|
||||
description: 'The desired privileges to bestow upon the given users.',
|
||||
coerce: ->(p) { p.is_a?(Integer) ? p : RemoteManagement::BitMask.mask_from_privileges(p) },
|
||||
callbacks: { 'is invalid. See https://ss64.com/osx/kickstart.html for valid privileges' => ->(p) { RemoteManagement::BitMask.valid_mask?(p) } }
|
||||
|
||||
property :computer_info,
|
||||
[String, Array],
|
||||
default: [],
|
||||
description: 'Info fields; helpful for stratifing computers in ARD client app.',
|
||||
coerce: ->(p) { p.compact.map(&:to_s) },
|
||||
callbacks: { 'has too many elements; computer info excepts up to four info fields' => ->(p) { (p.is_a?(Array) && p.size < 4) } }
|
||||
|
||||
load_current_value do |desired|
|
||||
current_value_does_not_exist! unless RemoteManagement.activated?
|
||||
privileges RemoteManagement.current_mask(desired.users)
|
||||
computer_info RemoteManagement.current_computer_info
|
||||
end
|
||||
|
||||
# TODO; the enable action should be decoupled from configuration; configure action should be added; default action should be [:configure, :enable]
|
||||
|
||||
action :enable do
|
||||
converge_if_changed(:privileges, :computer_info) do
|
||||
raise(RemoteManagement::Exceptions::TCCError) unless RemoteManagement::TCC::DB.correct_privileges?
|
||||
|
||||
execute 'restart the TCC daemon' do
|
||||
command 'sudo pkill -9 tccd'
|
||||
only_if { platform_version >= Chef::Version.new('12.0.0') }
|
||||
not_if { RemoteManagement::TCC::State.enabled? }
|
||||
end
|
||||
|
||||
converge_if_changed(:privileges) do
|
||||
if new_resource.users.include?('all')
|
||||
converge_by('setting privileges for all users') do
|
||||
execute 'set privileges for all users' do
|
||||
command [RemoteManagement.kickstart, '-configure', '-allowAccessFor', '-allUsers', '-access', '-on', '-privs', '-mask', new_resource.privileges]
|
||||
end
|
||||
end
|
||||
else
|
||||
converge_by('setting privileges for specified users') do
|
||||
execute 'set up Remote Management to only grant access to users with privileges' do
|
||||
command [RemoteManagement.kickstart, '-configure', '-allowAccessFor', '-specifiedUsers']
|
||||
end
|
||||
|
||||
execute "set privileges for #{new_resource.users.join(', ')}" do
|
||||
command [RemoteManagement.kickstart, '-configure', '-access', '-on', '-privs', '-mask', new_resource.privileges, '-users', new_resource.users.join(',')]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
converge_if_changed(:computer_info) do
|
||||
new_resource.computer_info.each_with_index do |info, i|
|
||||
execute "set computer info field #{i + 1}" do
|
||||
command [RemoteManagement.kickstart, '-configure', '-computerinfo', "-set#{i + 1}", "-#{i + 1}", info]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
execute 'activate the Remote Management service and restart the agent' do
|
||||
command [RemoteManagement.kickstart, '-activate', '-restart', '-agent']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
action :disable do
|
||||
execute 'stop the Remote Management service and deactivate it so it will not start after the next restart' do
|
||||
command [RemoteManagement.kickstart, '-deactivate', '-stop']
|
||||
only_if { RemoteManagement.activated? }
|
||||
end
|
||||
end
|
|
@ -0,0 +1,307 @@
|
|||
require_relative '../../spec_helper'
|
||||
|
||||
describe MacOS::RemoteManagement do
|
||||
let(:shellout) { double(stdout: nil, stderr: nil) }
|
||||
|
||||
describe '.current_mask' do
|
||||
context 'when ARD is currently configured to use global privileges' do
|
||||
let(:global_settings_xml) do
|
||||
<<~XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>ARD_AllLocalUsers</key>
|
||||
<true/>
|
||||
<key>ARD_AllLocalUsersPrivs</key>
|
||||
<integer>3</integer>
|
||||
<key>allowInsecureDH</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
XML
|
||||
end
|
||||
|
||||
it 'should retrieve the privilege value from global settings plist and return the equivalent mask' do
|
||||
allow(described_class).to receive(:global_settings_xml).and_return global_settings_xml
|
||||
expect(described_class.current_mask('bilbo')).to eq(-2147483645) # priv value: 3 -> priv mask: -2147483645
|
||||
end
|
||||
end
|
||||
context 'when ARD is currently configured to use individual privileges' do
|
||||
let(:dscl_naprivs) do
|
||||
<<~STDOUT
|
||||
bilbo -2147483644
|
||||
gandalf -2147483644
|
||||
STDOUT
|
||||
end
|
||||
|
||||
it 'should return the mask retrieved from `dscl`' do
|
||||
allow(described_class).to receive(:using_global_privileges?).and_return false
|
||||
allow(shellout).to receive(:stdout).and_return dscl_naprivs
|
||||
allow_any_instance_of(Chef::Mixin::ShellOut).to receive(:shell_out!).and_return shellout
|
||||
expect(described_class.current_mask(['bilbo', 'gandalf'])).to eq(-2147483644)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.current_users_configured?' do
|
||||
context 'when using global settings' do
|
||||
let(:global_settings_xml) do
|
||||
<<~XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>ARD_AllLocalUsers</key>
|
||||
<true/>
|
||||
<key>ARD_AllLocalUsersPrivs</key>
|
||||
<integer>3</integer>
|
||||
<key>allowInsecureDH</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
XML
|
||||
end
|
||||
|
||||
it 'should return true when global privilege value exists' do
|
||||
allow(shellout).to receive(:stdout).and_return global_settings_xml
|
||||
allow_any_instance_of(Chef::Mixin::ShellOut).to receive(:shell_out!).and_return shellout
|
||||
expect(described_class.current_users_configured?(['bilbo'])).to be true
|
||||
end
|
||||
end
|
||||
context 'when using individual settings' do
|
||||
context 'when all desired users are configured' do
|
||||
it 'should return true' do
|
||||
allow(described_class).to receive(:using_global_privileges?).and_return false
|
||||
allow(described_class).to receive(:individual_settings).and_return({ 'bilbo' => 1 })
|
||||
expect(described_class.current_users_configured?(['bilbo'])).to be true
|
||||
end
|
||||
end
|
||||
context 'when desired users are not configured' do
|
||||
it 'should return false' do
|
||||
allow(described_class).to receive(:using_global_privileges?).and_return false
|
||||
allow(described_class).to receive(:individual_settings).and_return({ 'bilbo' => 1 })
|
||||
expect(described_class.current_users_configured?(['bilbo', 'frodo'])).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.users_have_identical_masks?' do
|
||||
before(:each) { allow(described_class).to receive(:using_global_privileges?).and_return false }
|
||||
|
||||
context 'when the users have different privilege masks' do
|
||||
let(:dscl_naprivs) do
|
||||
<<~STDOUT
|
||||
bilbo -2147483644
|
||||
gandalf -2147483648
|
||||
STDOUT
|
||||
end
|
||||
|
||||
it 'should return false' do
|
||||
allow(shellout).to receive(:stdout).and_return dscl_naprivs
|
||||
allow_any_instance_of(Chef::Mixin::ShellOut).to receive(:shell_out!).and_return shellout
|
||||
expect(described_class.current_users_have_identical_masks?(['bilbo', 'gandalf'])).to be false
|
||||
end
|
||||
end
|
||||
context 'when the users have the same privilege masks' do
|
||||
let(:dscl_naprivs) do
|
||||
<<~STDOUT
|
||||
bilbo -2147483644
|
||||
ganalf -2147483644
|
||||
STDOUT
|
||||
end
|
||||
|
||||
it 'should return true' do
|
||||
allow(shellout).to receive(:stdout).and_return dscl_naprivs
|
||||
allow_any_instance_of(Chef::Mixin::ShellOut).to receive(:shell_out!).and_return shellout
|
||||
expect(described_class.current_users_have_identical_masks?(['bilbo', 'ganalf'])).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.current_computer_info' do
|
||||
context 'when there is no computer info' do
|
||||
let(:computer_info_xml) do
|
||||
<<~XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>DOCAllowRemoteConnections</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
XML
|
||||
end
|
||||
|
||||
it 'should return an empty array' do
|
||||
allow(shellout).to receive(:stdout).and_return computer_info_xml
|
||||
allow_any_instance_of(Chef::Mixin::ShellOut).to receive(:shell_out!).and_return shellout
|
||||
expect(described_class.current_computer_info).to eq []
|
||||
end
|
||||
end
|
||||
context 'when there is computer info' do
|
||||
let(:computer_info_xml) do
|
||||
<<~XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>DOCAllowRemoteConnections</key>
|
||||
<false/>
|
||||
<key>Text1</key>
|
||||
<string>bilbo</string>
|
||||
<key>Text2</key>
|
||||
<string>baggins</string>
|
||||
<key>Text3</key>
|
||||
<string></string>
|
||||
<key>Text4</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
||||
XML
|
||||
end
|
||||
|
||||
it 'should return an empty array' do
|
||||
allow(shellout).to receive(:stdout).and_return computer_info_xml
|
||||
allow_any_instance_of(Chef::Mixin::ShellOut).to receive(:shell_out!).and_return shellout
|
||||
expect(described_class.current_computer_info).to eq ['bilbo', 'baggins']
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.activated?' do
|
||||
context 'when the Remote Management launchd file does not exit' do
|
||||
it 'should return false' do
|
||||
allow(Chef).to receive(:node).and_return({'platform_version' => '12.6.3' })
|
||||
allow(RemoteManagement::TCC::State).to receive(:enabled?).and_return true
|
||||
allow(File).to receive(:exist?).and_call_original
|
||||
allow(File).to receive(:exist?).and_return false
|
||||
expect(described_class.activated?).to be false
|
||||
end
|
||||
end
|
||||
context 'when the tcc state is not enabled' do
|
||||
let(:tccstate_stdout) do
|
||||
<<~XML
|
||||
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
||||
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
|
||||
<plist version=\"1.0\">
|
||||
<dict>
|
||||
<key>postEvent</key>
|
||||
<false/>
|
||||
<key>screenCapture</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
XML
|
||||
end
|
||||
|
||||
it 'should return false' do
|
||||
allow(Chef).to receive(:node).and_return({'platform_version' => '12.6.3' })
|
||||
allow(File).to receive(:exist?).and_call_original
|
||||
allow(File).to receive(:exist?).and_return false
|
||||
allow(shellout).to receive(:stdout).and_return tccstate_stdout
|
||||
allow_any_instance_of(Chef::Mixin::ShellOut).to receive(:shell_out!).and_return shellout
|
||||
expect(described_class.activated?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe MacOS::RemoteManagement::Privileges do
|
||||
describe '.format_privileges' do
|
||||
it 'should strings to capitalized snakecase' do
|
||||
expect(described_class.format('OneRINGToRuleThemAll')).to eq [:ONE_RING_TO_RULE_THEM_ALL]
|
||||
expect(described_class.format('one__RING__to__find__them')).to eq [:ONE_RING_TO_FIND_THEM]
|
||||
expect(described_class.format('one RING to bring them all')).to eq [:ONE_RING_TO_BRING_THEM_ALL]
|
||||
expect(described_class.format('and-in-the-darkness-bind-them')).to eq [:AND_IN_THE_DARKNESS_BIND_THEM]
|
||||
end
|
||||
end
|
||||
|
||||
describe '.valid?' do
|
||||
context 'when the privileges are valid' do
|
||||
it 'should return true' do
|
||||
expect(described_class.valid?(:TEXT_MESSAGES)).to be true
|
||||
expect(described_class.valid?([:SEND_FILES, :CHANGE_SETTINGS])).to be true
|
||||
end
|
||||
end
|
||||
context 'when the privileges are invalid' do
|
||||
it 'should return true' do
|
||||
expect(described_class.valid?(:NASGUL)).to be false
|
||||
expect(described_class.valid?([:goblin, :orc])).to be false
|
||||
expect(described_class.valid?([:SMAUG])).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe MacOS::RemoteManagement::Privileges::Mask do
|
||||
describe '.valid?' do
|
||||
subject { described_class.new(mask: -2147483645) }
|
||||
|
||||
context 'when the mask is valid' do
|
||||
it 'should return true' do
|
||||
expect(subject.valid?(-2147483645)).to be true
|
||||
expect(subject.valid?(-2147483552)).to be true
|
||||
expect(subject.valid?(-2147483508)).to be true
|
||||
expect(subject.valid?(-1073741569)).to be true
|
||||
expect(subject.valid?(-2147483648)).to be true
|
||||
expect(subject.valid?(-2147483392)).to be true
|
||||
expect(subject.valid?(-2147483391)).to be true
|
||||
end
|
||||
end
|
||||
context 'when the mask is invalid' do
|
||||
it 'should return false' do
|
||||
expect(subject.valid?(-1073725185)).to be false
|
||||
expect(subject.valid?(-2145386496)).to be false
|
||||
expect(subject.valid?(-2143813632)).to be false
|
||||
expect(subject.valid?(-2143813629)).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.to_a' do
|
||||
it 'should return an array of privilege strings coresponding to the privilege mask' do
|
||||
expect(described_class.new(mask: -2147483645).to_a).to contain_exactly('TextMessages', 'ControlObserve')
|
||||
expect(described_class.new(mask: -2147483552).to_a).to contain_exactly('OpenQuitApps', 'ChangeSettings')
|
||||
expect(described_class.new(mask: -2147483508).to_a).to contain_exactly('SendFiles', 'RestartShutDown', 'DeleteFiles')
|
||||
expect(described_class.new(mask: -1073741569).to_a).to contain_exactly('all')
|
||||
expect(described_class.new(mask: -2147483648).to_a).to contain_exactly('none')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe MacOS::RemoteManagement::Privileges::Value do
|
||||
describe '.valid?' do
|
||||
subject { described_class.new(value: 0) }
|
||||
|
||||
context 'when the value is valid' do
|
||||
it 'should return true' do
|
||||
expect(subject.valid?(3)).to be true
|
||||
expect(subject.valid?(96)).to be true
|
||||
expect(subject.valid?(140)).to be true
|
||||
expect(subject.valid?(0)).to be true
|
||||
expect(subject.valid?(1073742079)).to be true
|
||||
end
|
||||
end
|
||||
context 'when the mask is invalid' do
|
||||
it 'should return false' do
|
||||
expect(subject.valid?(512)).to be false
|
||||
expect(subject.valid?(888)).to be false
|
||||
expect(subject.valid?(1024)).to be false
|
||||
expect(subject.valid?(666)).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.to_a' do
|
||||
it 'should return an array of privilege strings coresponding to the privilege value' do
|
||||
expect(described_class.new(value: 3).to_a).to contain_exactly('TextMessages', 'ControlObserve')
|
||||
expect(described_class.new(value: 96).to_a).to contain_exactly('OpenQuitApps', 'ChangeSettings')
|
||||
expect(described_class.new(value: 140).to_a).to contain_exactly('SendFiles', 'RestartShutDown', 'DeleteFiles')
|
||||
expect(described_class.new(value: 1073742079).to_a).to contain_exactly('all')
|
||||
expect(described_class.new(value: 0).to_a).to contain_exactly('none')
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,102 +1,383 @@
|
|||
require 'spec_helper'
|
||||
require_relative '../../spec_helper'
|
||||
|
||||
shared_context 'with remote management enabled' do
|
||||
step_into :remote_management
|
||||
platform 'mac_os_x'
|
||||
shared_context 'remote management enabled' do
|
||||
before { allow(RemoteManagement).to receive(:activated?).and_return true }
|
||||
end
|
||||
|
||||
shared_context 'remote management disabled' do
|
||||
before { allow(RemoteManagement).to receive(:activated?).and_return false }
|
||||
end
|
||||
|
||||
shared_context 'users current mask is -2147483648 (no privileges)' do
|
||||
let(:current_mask) { RemoteManagement::Privileges::Mask.new(mask: -2147483648) }
|
||||
|
||||
before { allow(RemoteManagement).to receive(:current_users_configured?).and_return true }
|
||||
before { allow(RemoteManagement).to receive(:current_users_have_identical_masks?).and_return true }
|
||||
before { allow(RemoteManagement).to receive(:current_mask).and_return(current_mask) }
|
||||
end
|
||||
|
||||
shared_context 'users current mask is -1073741569 (all privileges)' do
|
||||
let(:current_mask) { RemoteManagement::Privileges::Mask.new(mask: -1073741569) }
|
||||
|
||||
before { allow(RemoteManagement).to receive(:current_users_configured?).and_return true }
|
||||
before { allow(RemoteManagement).to receive(:current_users_have_identical_masks?).and_return true }
|
||||
before { allow(RemoteManagement).to receive(:current_mask).and_return(current_mask) }
|
||||
end
|
||||
|
||||
shared_context 'no current computer info' do
|
||||
before { allow(RemoteManagement).to receive(:current_computer_info).and_return [] }
|
||||
end
|
||||
|
||||
shared_context 'SIP is disabled' do
|
||||
before { allow(RemoteManagement::TCC::SIP).to receive(:disabled?).and_return true }
|
||||
end
|
||||
|
||||
shared_context 'correct tccstate' do
|
||||
before { allow(RemoteManagement::TCC::State).to receive(:enabled?).and_return true }
|
||||
end
|
||||
|
||||
shared_context 'incorrect tccstate' do
|
||||
before { allow(RemoteManagement::TCC::State).to receive(:enabled?).and_return false }
|
||||
end
|
||||
|
||||
shared_context 'correct TCC database privileges' do
|
||||
before do
|
||||
allow(File).to receive(:exist?).and_call_original
|
||||
allow(File).to receive(:exist?)
|
||||
.with('/Library/Application Support/Apple/Remote Desktop/RemoteManagement.launchd')
|
||||
.and_return(true)
|
||||
allow(RemoteManagement).to receive(:plist_content)
|
||||
.and_return 'Dict { ARD_AllLocalUsersPrivs = 1073742079
|
||||
ARD_AllLocalUsers = true }'
|
||||
allow(RemoteManagement::TCC::DB).to receive(:correct_privileges?).and_return true
|
||||
end
|
||||
end
|
||||
|
||||
shared_context 'with remote management disabled' do
|
||||
step_into :remote_management
|
||||
platform 'mac_os_x'
|
||||
|
||||
shared_context 'incorrect TCC database privileges' do
|
||||
before do
|
||||
allow(File).to receive(:exist?).and_call_original
|
||||
allow(File).to receive(:exist?)
|
||||
.with('/Library/Application Support/Apple/Remote Desktop/RemoteManagement.launchd')
|
||||
.and_return(false)
|
||||
allow(RemoteManagement).to receive(:plist_content)
|
||||
.and_return ''
|
||||
allow(RemoteManagement::TCC::DB).to receive(:correct_privileges?).and_return false
|
||||
allow(RemoteManagement::TCC::DB).to receive(:screensharing_client_authorized_for_post_event_service?).and_return false
|
||||
allow(RemoteManagement::TCC::DB).to receive(:screensharing_client_authorized_for_screencapture_service?).and_return false
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'kickstart activating and configuring the ARD agent' do
|
||||
it { is_expected.to run_execute('/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart -activate') }
|
||||
it { is_expected.to run_execute('/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart -configure -allowAccessFor -allUsers -access -on -privs -all') }
|
||||
shared_examples 'activating the ARD agent' do
|
||||
it {
|
||||
is_expected.to run_execute('activate the Remote Management service and restart the agent')
|
||||
.with(command: ['/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart', '-activate', '-restart', '-agent'])
|
||||
}
|
||||
end
|
||||
|
||||
shared_examples 'kickstart deactivating and stopping the ARD agent' do
|
||||
it { is_expected.to run_execute('/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart -deactivate') }
|
||||
it { is_expected.to run_execute('/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart -stop') }
|
||||
shared_examples 'not activating the ARD agent' do
|
||||
it { is_expected.to_not run_execute('activate the Remote Management service and restart the agent') }
|
||||
end
|
||||
|
||||
shared_examples 'kickstart not activating or configuring the ARD agent' do
|
||||
it { is_expected.to_not run_execute('/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart -activate') }
|
||||
it { is_expected.to_not run_execute('/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart -configure -allowAccessFor -allUsers -access -on -privs -all') }
|
||||
shared_examples 'configuring the ARD agent for all users' do
|
||||
it {
|
||||
is_expected.to run_execute('set privileges for all users')
|
||||
.with(command: ['/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart', '-configure', '-allowAccessFor', '-allUsers', '-access', '-on', '-privs', '-all'])
|
||||
}
|
||||
end
|
||||
|
||||
shared_examples 'kickstart not deactivating or stopping the ARD agent' do
|
||||
it { is_expected.to_not run_execute('/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart -deactivate') }
|
||||
it { is_expected.to_not run_execute('/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart -stop') }
|
||||
shared_examples 'not configuring the ARD agent for all users' do
|
||||
it { is_expected.to_not run_execute('set privileges for all users') }
|
||||
end
|
||||
|
||||
describe 'enabling when already disabled' do
|
||||
include_context 'with remote management disabled'
|
||||
shared_examples 'configuring the ARD agent for specified users' do
|
||||
it {
|
||||
is_expected.to run_execute('set up Remote Management to only grant access to users with privileges')
|
||||
.with(command: ['/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart', '-configure', '-allowAccessFor', '-specifiedUsers'])
|
||||
}
|
||||
it {
|
||||
is_expected.to run_execute('set privileges for bilbo')
|
||||
.with(command: ['/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart', '-configure', '-access', '-on', '-privs', '-all', '-users', 'bilbo'])
|
||||
}
|
||||
end
|
||||
|
||||
shared_examples 'not configuring the ARD agent for specified users' do
|
||||
it { is_expected.to_not run_execute('set up Remote Management to only grant access to users with privileges') }
|
||||
it { is_expected.to_not run_execute('set privileges for bilbo') }
|
||||
end
|
||||
|
||||
shared_examples 'restarting the TCC daemon' do
|
||||
it {
|
||||
is_expected.to run_execute('restart the TCC daemon')
|
||||
.with(command: 'sudo pkill -9 tccd')
|
||||
}
|
||||
end
|
||||
|
||||
shared_examples 'not restarting the TCC daemon' do
|
||||
it { is_expected.to_not run_execute('restart the TCC daemon') }
|
||||
end
|
||||
|
||||
shared_examples 'deactivating and stopping the ARD agent' do
|
||||
it {
|
||||
is_expected.to run_execute('stop the Remote Management service and deactivate it so it will not start after the next restart')
|
||||
.with(command: ['/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart', '-deactivate', '-stop'])
|
||||
}
|
||||
end
|
||||
|
||||
shared_examples 'not deactivating or stopping the ARD agent' do
|
||||
it { is_expected.to_not run_execute('stop the Remote Management service and deactivate it so it will not start after the next restart') }
|
||||
end
|
||||
|
||||
shared_examples 'setting the computer info' do
|
||||
it {
|
||||
is_expected.to run_execute('set computer info field 1')
|
||||
.with(command: ['/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart', '-configure', '-computerinfo', '-set1', '-1', 'Arkenstone'])
|
||||
}
|
||||
end
|
||||
|
||||
shared_examples 'not setting the computer info' do
|
||||
it { is_expected.to_not run_execute('set computer info 1') }
|
||||
end
|
||||
|
||||
describe 'enabling for all users when currently disabled' do
|
||||
platform 'mac_os_x', '12'
|
||||
step_into :remote_management
|
||||
|
||||
include_context 'remote management disabled'
|
||||
include_context 'users current mask is -2147483648 (no privileges)'
|
||||
include_context 'no current computer info'
|
||||
include_context 'correct TCC database privileges'
|
||||
include_context 'incorrect tccstate'
|
||||
include_context 'SIP is disabled'
|
||||
|
||||
recipe do
|
||||
remote_management 'enabled' do
|
||||
remote_management 'enable the ARD agent, giving all users all privileges' do
|
||||
users 'all'
|
||||
privileges 'all'
|
||||
computer_info []
|
||||
action :enable
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'kickstart activating and configuring the ARD agent'
|
||||
it_behaves_like 'kickstart not deactivating or stopping the ARD agent'
|
||||
it_behaves_like 'configuring the ARD agent for all users'
|
||||
it_behaves_like 'restarting the TCC daemon'
|
||||
it_behaves_like 'activating the ARD agent'
|
||||
|
||||
it_behaves_like 'not configuring the ARD agent for specified users'
|
||||
it_behaves_like 'not setting the computer info'
|
||||
end
|
||||
|
||||
describe 'enabling when already enabled' do
|
||||
include_context 'with remote management enabled'
|
||||
describe 'enabling for specified users when currently disabled' do
|
||||
platform 'mac_os_x', '12'
|
||||
step_into :remote_management
|
||||
|
||||
include_context 'remote management disabled'
|
||||
include_context 'users current mask is -2147483648 (no privileges)'
|
||||
include_context 'no current computer info'
|
||||
include_context 'correct TCC database privileges'
|
||||
include_context 'correct tccstate'
|
||||
include_context 'SIP is disabled'
|
||||
|
||||
recipe do
|
||||
remote_management 'enabled' do
|
||||
remote_management 'enable the ARD agent' do
|
||||
users 'bilbo'
|
||||
privileges 'all'
|
||||
computer_info []
|
||||
action :enable
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'kickstart not activating or configuring the ARD agent'
|
||||
it_behaves_like 'kickstart not deactivating or stopping the ARD agent'
|
||||
it_behaves_like 'configuring the ARD agent for specified users'
|
||||
it_behaves_like 'not restarting the TCC daemon'
|
||||
it_behaves_like 'activating the ARD agent'
|
||||
|
||||
it_behaves_like 'not configuring the ARD agent for all users'
|
||||
it_behaves_like 'not setting the computer info'
|
||||
end
|
||||
|
||||
describe 'disabling when already disabled' do
|
||||
include_context 'with remote management disabled'
|
||||
describe 'enabling for specified users when currently enabled and current privileges are different than the desired privileges' do
|
||||
platform 'mac_os_x', '12'
|
||||
step_into :remote_management
|
||||
|
||||
include_context 'remote management enabled'
|
||||
include_context 'users current mask is -2147483648 (no privileges)'
|
||||
include_context 'no current computer info'
|
||||
include_context 'correct TCC database privileges'
|
||||
include_context 'incorrect tccstate'
|
||||
include_context 'SIP is disabled'
|
||||
|
||||
recipe do
|
||||
remote_management 'disabled' do
|
||||
remote_management 'enable the ARD agent' do
|
||||
users 'bilbo'
|
||||
privileges 'all'
|
||||
computer_info []
|
||||
action :enable
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'configuring the ARD agent for specified users'
|
||||
it_behaves_like 'restarting the TCC daemon'
|
||||
it_behaves_like 'activating the ARD agent'
|
||||
|
||||
it_behaves_like 'not configuring the ARD agent for all users'
|
||||
it_behaves_like 'not setting the computer info'
|
||||
end
|
||||
|
||||
describe 'enabling when the current computer info differs from the desired computer info' do
|
||||
platform 'mac_os_x', '12'
|
||||
step_into :remote_management
|
||||
|
||||
include_context 'remote management enabled'
|
||||
include_context 'users current mask is -2147483648 (no privileges)'
|
||||
include_context 'no current computer info'
|
||||
include_context 'correct TCC database privileges'
|
||||
include_context 'incorrect tccstate'
|
||||
include_context 'SIP is disabled'
|
||||
|
||||
recipe do
|
||||
remote_management 'enable the ARD agent' do
|
||||
users 'bilbo'
|
||||
privileges 'all'
|
||||
computer_info 'Arkenstone'
|
||||
action :enable
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'setting the computer info'
|
||||
end
|
||||
|
||||
describe 'enabling for specified users when currently enabled and current privileges are the same as the desired privileges' do
|
||||
platform 'mac_os_x', '12'
|
||||
step_into :remote_management
|
||||
|
||||
include_context 'remote management enabled'
|
||||
include_context 'users current mask is -1073741569 (all privileges)'
|
||||
include_context 'no current computer info'
|
||||
include_context 'correct TCC database privileges'
|
||||
include_context 'incorrect tccstate'
|
||||
include_context 'SIP is disabled'
|
||||
|
||||
recipe do
|
||||
remote_management 'enable the ARD agent' do
|
||||
users 'bilbo'
|
||||
privileges 'all'
|
||||
computer_info []
|
||||
action :enable
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'not configuring the ARD agent for specified users'
|
||||
it_behaves_like 'not configuring the ARD agent for all users'
|
||||
it_behaves_like 'not restarting the TCC daemon'
|
||||
it_behaves_like 'not activating the ARD agent'
|
||||
it_behaves_like 'not setting the computer info'
|
||||
end
|
||||
|
||||
describe 'trying to enable when TCC does not have the correct privileges' do
|
||||
platform 'mac_os_x', '12'
|
||||
step_into :remote_management
|
||||
|
||||
include_context 'remote management disabled'
|
||||
include_context 'users current mask is -2147483648 (no privileges)'
|
||||
include_context 'no current computer info'
|
||||
include_context 'incorrect TCC database privileges'
|
||||
include_context 'SIP is disabled'
|
||||
|
||||
recipe do
|
||||
remote_management 'enable the ARD agent' do
|
||||
users 'bilbo'
|
||||
privileges 'all'
|
||||
computer_info []
|
||||
action :enable
|
||||
end
|
||||
end
|
||||
|
||||
it { expect { subject }.to raise_error(RemoteManagement::Exceptions::TCCError) }
|
||||
end
|
||||
|
||||
describe 'trying to enable with invalid privileges' do
|
||||
platform 'mac_os_x', '12'
|
||||
step_into :remote_management
|
||||
|
||||
include_context 'remote management disabled'
|
||||
include_context 'users current mask is -2147483648 (no privileges)'
|
||||
include_context 'no current computer info'
|
||||
include_context 'incorrect TCC database privileges'
|
||||
include_context 'SIP is disabled'
|
||||
|
||||
recipe do
|
||||
remote_management 'enable the ARD agent' do
|
||||
users 'bilbo'
|
||||
privileges 'smaug'
|
||||
computer_info []
|
||||
action :enable
|
||||
end
|
||||
end
|
||||
|
||||
it { expect { subject }.to raise_error(Chef::Exceptions::ValidationFailed) }
|
||||
end
|
||||
|
||||
describe 'trying to enable with invalid privileges' do
|
||||
platform 'mac_os_x', '12'
|
||||
step_into :remote_management
|
||||
|
||||
include_context 'remote management disabled'
|
||||
include_context 'users current mask is -2147483648 (no privileges)'
|
||||
include_context 'no current computer info'
|
||||
include_context 'incorrect TCC database privileges'
|
||||
include_context 'SIP is disabled'
|
||||
|
||||
recipe do
|
||||
remote_management 'enable the ARD agent' do
|
||||
users 'bilbo'
|
||||
privileges ['nazgûl', 'smèagol']
|
||||
computer_info []
|
||||
action :enable
|
||||
end
|
||||
end
|
||||
|
||||
it { expect { subject }.to raise_error(Chef::Exceptions::ValidationFailed) }
|
||||
end
|
||||
|
||||
describe 'enable with abnormally formatted privileges' do
|
||||
platform 'mac_os_x', '12'
|
||||
step_into :remote_management
|
||||
|
||||
include_context 'remote management disabled'
|
||||
include_context 'users current mask is -2147483648 (no privileges)'
|
||||
include_context 'no current computer info'
|
||||
include_context 'correct TCC database privileges'
|
||||
include_context 'incorrect tccstate'
|
||||
include_context 'SIP is disabled'
|
||||
|
||||
recipe do
|
||||
remote_management 'enable the ARD agent' do
|
||||
users 'bilbo'
|
||||
privileges ['DeleteFiles', 'text messages', 'SEND__FILES', 'restart-shut-down']
|
||||
computer_info []
|
||||
action :enable
|
||||
end
|
||||
end
|
||||
|
||||
it { expect { subject }.to_not raise_error }
|
||||
end
|
||||
|
||||
describe 'disabling when currently disabled' do
|
||||
platform 'mac_os_x', '12'
|
||||
step_into :remote_management
|
||||
|
||||
include_context 'remote management disabled'
|
||||
include_context 'users current mask is -2147483648 (no privileges)'
|
||||
include_context 'SIP is disabled'
|
||||
|
||||
recipe do
|
||||
remote_management 'disable the ARD agent' do
|
||||
action :disable
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'kickstart not activating or configuring the ARD agent'
|
||||
it_behaves_like 'kickstart not deactivating or stopping the ARD agent'
|
||||
it_behaves_like 'not deactivating or stopping the ARD agent'
|
||||
end
|
||||
|
||||
describe 'disabling when already enabled' do
|
||||
include_context 'with remote management enabled'
|
||||
describe 'disabling when currently enabled' do
|
||||
platform 'mac_os_x', '12'
|
||||
step_into :remote_management
|
||||
|
||||
include_context 'remote management enabled'
|
||||
include_context 'users current mask is -2147483648 (no privileges)'
|
||||
include_context 'SIP is disabled'
|
||||
|
||||
recipe do
|
||||
remote_management 'disabled' do
|
||||
remote_management 'disable the ARD agent' do
|
||||
action :disable
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'kickstart not activating or configuring the ARD agent'
|
||||
it_behaves_like 'kickstart deactivating and stopping the ARD agent'
|
||||
it_behaves_like 'deactivating and stopping the ARD agent'
|
||||
end
|
||||
|
|
|
@ -1,4 +1,22 @@
|
|||
remote_management 'activate and configure remote management for all users' do
|
||||
action :enable
|
||||
not_if { node['platform_version'] >= '11.0' } # remove prior to Big Sur public release
|
||||
# TODO; do we want to add the TCC logic to the resource?
|
||||
|
||||
tcc_db_path = '/Library/Application Support/com.apple.TCC/TCC.db'
|
||||
|
||||
execute 'authorize screensharing client to utilize the kTCCServicePostEvent service' do
|
||||
command ['/usr/bin/sqlite3', tcc_db_path, "INSERT OR REPLACE INTO access VALUES('kTCCServicePostEvent','com.apple.screensharing.agent',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1639743960);" ]
|
||||
not_if { RemoteManagement::TCC::DB.correct_privileges? }
|
||||
only_if { shell_out('/usr/sbin/system_profiler', 'SPSoftwareDataType').stdout.match?(Regexp.new('System Integrity Protection: Disabled')) }
|
||||
end
|
||||
|
||||
execute 'authorize screensharing client to utilize the kTCCServiceScreenCapture service' do
|
||||
command ['/usr/bin/sqlite3', tcc_db_path, "INSERT OR REPLACE INTO access VALUES ('kTCCServiceScreenCapture','com.apple.screensharing.agent',0,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1639743960);" ]
|
||||
not_if { RemoteManagement::TCC::DB.correct_privileges? }
|
||||
only_if { shell_out('/usr/sbin/system_profiler', 'SPSoftwareDataType').stdout.match?(Regexp.new('System Integrity Protection: Disabled')) }
|
||||
end
|
||||
|
||||
remote_management 'activate and configure remote management for all users' do
|
||||
users 'all'
|
||||
privileges %w(text__messages ControlObserve send_files)
|
||||
computer_info ['Arkenstone', 'Gold']
|
||||
only_if { shell_out('/usr/sbin/system_profiler', 'SPSoftwareDataType').stdout.match?(Regexp.new('System Integrity Protection: Disabled')) }
|
||||
end
|
||||
|
|
|
@ -1,31 +1,23 @@
|
|||
title 'remote access'
|
||||
|
||||
# user_has_access = 1 << 31
|
||||
text_messages = 1 << 0
|
||||
control_observe = 1 << 1
|
||||
send_files = 1 << 2
|
||||
delete_files = 1 << 3
|
||||
generate_reports = 1 << 4
|
||||
open_quit_apps = 1 << 5
|
||||
change_settings = 1 << 6
|
||||
restart_shutdown = 1 << 7
|
||||
# observe_only = 1 << 8
|
||||
show_observe = 1 << 30
|
||||
|
||||
all_privileges = text_messages | control_observe | send_files |
|
||||
delete_files | generate_reports | open_quit_apps |
|
||||
change_settings | restart_shutdown | show_observe
|
||||
|
||||
control 'remote-control' do
|
||||
title 'ensure that remote access and control will function'
|
||||
desc "ensure that the Remote Management plist grants local users access, that
|
||||
all privileges are granted based on the mask #{all_privileges}, and that
|
||||
remote control is enabled"
|
||||
only_if { os.release < '20' } # remove prior to Big Sur public release
|
||||
title 'ensure the correct configuration of ARD'
|
||||
desc 'ensure that the ARD is enabled and configured to grant the correct privileges to the correct users'
|
||||
|
||||
describe command('/usr/libexec/PlistBuddy -c Print /Library/Preferences/com.apple.RemoteManagement.plist') do
|
||||
its('stdout') { should match 'ARD_AllLocalUsers = true' }
|
||||
its('stdout') { should match /#{all_privileges}/ }
|
||||
describe command('/usr/bin/defaults read /Library/Preferences/com.apple.RemoteManagement') do
|
||||
its('stdout') { should match /"ARD_AllLocalUsers" = 1/ }
|
||||
end
|
||||
|
||||
describe command('/usr/bin/defaults read /Library/Preferences/com.apple.RemoteDesktop') do
|
||||
its('stdout') { should match /Text1 = \w+/ }
|
||||
its('stdout') { should match /Text2 = \w+/ }
|
||||
its('stdout') { should match /Text3 = ""/ }
|
||||
its('stdout') { should match /Text4 = ""/ }
|
||||
end
|
||||
|
||||
if Chef::Version.new(os.release) >= Chef::Version.new('21.0.0')
|
||||
describe command('sudo /System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Support/tccstate') do
|
||||
its('stdout') { should match Regexp.new('<key>postEvent<\/key>.\s+<true\/>', Regexp::MULTILINE) }
|
||||
its('stdout') { should match Regexp.new('<key>screenCapture<\/key>.\s+<true\/>', Regexp::MULTILINE) }
|
||||
end
|
||||
end
|
||||
|
||||
describe file('/Library/Application Support/Apple/Remote Desktop/RemoteManagement.launchd') do
|
||||
|
|
Загрузка…
Ссылка в новой задаче