├── LICENSE
├── README.md
├── compliance.png
├── exempt1.png
├── exempt2.png
├── exempt3.png
├── policylist.png
└── smartgroup.png
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Tony Williams
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | ### How To Incorporate the 'macOS Security Compliance Project' Into Jamf Pro
3 |
4 | In this document I will show how to use the NIST tool chain with the CIS level 2 benchmark. You can find the tools at `https://github.com/usnistgov/macos_security`. The method will work with any other baseline in the project, just replace `cis_lvl2` everywhere below with the name of the baseline you want to use.
5 | #### Grab the Tools
6 |
7 | Go in to whatever directory you store your git repositories and `git clone git@github.com:usnistgov/macos_security` to pull down the latest version of the toolset. Then you will need some gems, `bundle install` will grab everything required. Finally some Python requirements, `pip3 install -r requirements.txt --user`. (You could check https://github.com/usnistgov/macos_security/wiki/Getting-Started to see if anything has changed.)
8 |
9 | #### Before You Build
10 |
11 | To do this properly you should communicate with your Security team exactly what you are doing. The first step is to print the entire set of security rules in the benchmark. To do this you create a PDF by running the guidance script on the base YAML file. Go into the top level of the repo and ...
12 | ```
13 | ~/macos_security $ ./scripts/generate_guidance.py baselines/cis_lvl2.yaml
14 | ```
15 | You will then see:
16 | ```
17 | Profile YAML: baselines/cis_lvl2.yaml
18 | Output path: /Users/puck/work/macos_security/build/cis_lvl2/cis_lvl2.adoc
19 | Generating HTML file from AsciiDoc...
20 | Generating PDF file from AsciiDoc...
21 | ```
22 | Now there will be a PDF file in `build/cis_lvl2` called `cis_lvl2.pdf`
23 |
24 | Go through this file and write down the rule number and title for all the rules you don't think are needed in your organisation. At my company we decided "7.21 Enable Show All Filename Extensions" would confuse users. There may be others, CIS level 2 is a tight benchmark.
25 |
26 | Take this list and the PDF file and get Security to check if they are fine with the baseline and your changes to it. (It might be best to ask them if they have a baseline they recommend _before_ you do anything but I forgot that step.)
27 |
28 | #### Now On To Building
29 |
30 | Once you have finished that argument (I hope without too much bloodshed) take the file `cis_lvl2_yaml` and copy it to the custom directory with a new name that tells you it is yours:
31 | ```
32 | ~/macos_security $ cp baselines/cis_lvl2.yaml custom/cis_lvl2_puck.yaml
33 | ```
34 |
35 | Open the copy and comment all the lines for the rules you don't want (put '# ' at the start of the line). For our example rule, the line containing `os_show_filename_extensions_enable`.
36 |
37 | We now run the generate guidance script. Point it at the changed YAML file and add arguments to generate the config profiles (`-p`) and the compliance script (`-s`).
38 | ```
39 | $ scripts/generate_guidance.py -p -s custom/cis_lvl2_puck.yaml
40 | ```
41 |
42 | This will generate a new directory inside `build` with the name of your YAML file. Inside is a directory containing the mobileconfig profiles, the compliance script, the documentation files, and a folder "preferences" that we can safely ignore for now.
43 |
44 | ```
45 | $ ls build/cis_lvl2_puck/
46 | puck.adoc cis_lvl2_puck.html cis_lvl2_puck.pdf
47 | cis_lvl2_puck_compliance.sh mobileconfigs preferences
48 | ```
49 |
50 | ### Implementation
51 |
52 | ##### Some Tedious Stuff
53 |
54 | Now we are ready to start in on our Jamf Pro set up. I assume I don't need to tell you to do all this on your test instance not production.
55 |
56 | First step is to create a category for all our scripts, policies and profiles. Mine is `CIS v2`. Also create a Computer Group called CIS v2 TEST and scope everything to that group; profiles, policies, the lot. Assign a test Mac or two to that group.
57 |
58 | Now we are going to create a configuration profile that sets everything we can set in the Jamf GUI. I call mine `0.0 CIS2 Monterey Restrictions`
59 |
60 | Open the file `cis_lvl2_puck.pdf` in Preview. Go through all the rules and where possible set them in our profile. All of section 6 can be skipped over but right off the bat `7.1 Disable Airdrop` can be set in the `Restrictions` payload of our profile. Go through the rest one by one. Don't worry right now about any rules you can't set.
61 |
62 | Now go through the rules a second time.
63 |
64 | Some of the rules you can't set it in the Jamf GUI but you can set with a custom settings upload. For these create a separate config profile titled with the number and name of the rule.
65 |
66 | An example of this would be the rule I have, "7.06 Enable Firewall Logging". You can see in the "Remediation Description" that we should create a config profile and the payload is `com.apple.security.firewall`. Look in the directory `build\cis_lvl2_puck\mobileconfigs\preferences` for a file with the same name as the payload. Create the profile "7.0.6 Enable Firewall Logging" and in the section "Application & Custom Settings" add an "Upload". The "Preference Domain" is the name of our file (minus the ".plist") and the contents of the file is copied into the "Property List" field. Save the profile.
67 |
68 | This is the last of the truly tedious stuff.
69 |
70 | ##### The Script
71 |
72 | We are almost ready to upload our script. We need to remove some code in the compliance script as it incorrectly checks the number of command line arguments:
73 | ```
74 | # check for command line arguments, if --check or --fix, then just do them.
75 | if (( # >= 2));then
76 | echo "Too many arguments. Usage: $0 [--check| --fix]"
77 | exit 1
78 | fi
79 | ```
80 |
81 | They are just above the only call to the `zparseopts` function.
82 |
83 | (It is entirely possible that by this time the lines have been removed from the git repo for you so if you don't see them don't worry.)
84 |
85 | Now go create a script in Jamf Pro. This can be found under Settings > Computer Management > Scripts > New Script.
86 | - Use the General pane to configure basic settings for the script, including the display name and assign to our previously created category.
87 | - Use the Script pane to paste in our compliance script “cis_lvl2_puck_compliance.sh".
88 | - Use the Options pane to set the Parameter 4 label to `Options (--check, --fix, --stats, --compliant, --non_compliant)`.
89 | ##### _The 'Options' pane of our script_
90 | 
91 | ##### More Pieces
92 |
93 | Next we create two Extension Attributes in Jamf Pro, one to count the non-compliant rules and one to list them. This can be found under Settings > Computer Management. In the “Computer Management–Management Framework” section, click Extension Attributes > New.
94 |
95 | - Set the Data type to String
96 | - Set the Input type to Script
97 | - Paste into the script section in the editor and save
98 | ```
99 | #!/bin/zsh
100 |
101 | # cis v2 - Audit List
102 | AUDIT_LIST=$(/usr/libexec/PlistBuddy -c "Print" /Library/Preferences/org.cis_lvl2_puck.audit.plist | grep -B 1 "finding = true")
103 | AUDIT_LIST_NEAT=$(echo "$AUDIT_LIST" | sed -n '/finding = true/!p' | sed -n '/--/!p' | cut -f1 -d"=" | column -t)
104 |
105 | echo "$AUDIT_LIST_NEAT"
106 |
107 | exit 0
108 | ```
109 | ```
110 | #!/bin/zsh
111 |
112 | # cis v2 - Audit Count
113 | echo ""
114 | /usr/libexec/PlistBuddy -c "Print" /Library/Preferences/org.cis_lvl2_puck.audit.plist |\
115 | grep -c "finding = true"
116 | echo ""
117 | ```
118 |
119 | It should be said that the compliance script contains a method for finding the count of non-compliant and compliant rules that is much more complex than the one above. I have yet to discover a reason for that.
120 |
121 | Now for a Smart Group that looks at the audit count.
122 | - Name: CIS v2 - Non-compliant
123 | - Criteria: EA cis v2 - Audit Count > 0
124 | #####
125 | 
126 | ##### Policies
127 |
128 | We want three policies. The first one run is "CISv2 Fix". It runs at enrollment complete and with a custom trigger of `cis_fix`. Then we have "CISv2 Check" which runs at Check-in and has a custom trigger of `cis_check`. These two policies run the compliance script. `cis_check` has `--check` in the options while the other has `--fix`
129 |
130 | Our final policy, "CISv2 Fix Controller", runs at Check-in and is scoped to "CIS v2 Non-compliant". This policy runs a script:
131 | ```
132 | #!/bin/zsh
133 |
134 | # v1.0 2020-04-29 ARW
135 |
136 | # write to fix log
137 | echo $(date) >> /Library/Management/cisfixlog.txt
138 | /usr/libexec/PlistBuddy -c "Print" /Library/Preferences/org.cis_lvl2_puck.audit.plist | \
139 | grep -B 1 "finding = true" >> /Library/Management/cisfixlog.txt
140 |
141 | # do the fix
142 | jamf policy -event cis_fix ;
143 |
144 | # do a recheck to pick up the remediation
145 | jamf policy -event cis_check
146 |
147 | # do a recon to update the EAs
148 | jamf recon
149 | ```
150 |
151 | In my SOE I use `/Library/Management` as a place for all the bits and pieces I use such as the company logo. If you use a different spot use that.
152 |
153 | The first half of the script logs the date and a list of the non-compliant rules to a text file. We can read `/Library/Management/cisfixlog.txt` in an EA if we want. Having this makes security happy as you are logging every breach.
154 |
155 | The second half is running `jamf` three times. You can see that we run the remediation, then run the check again. This check *should* pick up that the Mac is now compliant and write that out to our audit plist. Now a `jamf recon` will update our two EAs by making them run again. That means the Mac is no longer a member of our smart group.
156 |
157 | ##### _Our policy list_
158 | 
159 | ##### Testing
160 |
161 | For preliminary testing I turn on Self Service for the three policies. I also edit any other security policies and set them to exclude our test group, we want to be sure we get a clean test.
162 |
163 | To do our testing we want a rule that is easily broken and just as easily fixed but doesn't constitute a large breach of security. "Disable Root Login" is the perfect candidate. `/usr/bin/dscl . -create /Users/root UserShell /bin/zsh` will break it and the remediation done by our system is a single line `/usr/bin/dscl . -create /Users/root UserShell /usr/bin/false`.
164 |
165 | One note in testing is that the policy log is only written after a policy completes so even though "CISv2 Fix Control" runs first it will be above "CISv2 Fix" and "CISv2 Check" in the Policy Log.
166 |
167 | Once you have got it working by running the policies via self service in your test instance find some volunteers to run the system in production to make sure you're not breaking something with all the new security rules. If you are then figure out which rule it is and go back to Security.
168 |
169 | ##### Caveat
170 |
171 | In our organisation we use FileVault (as everyone should). This causes a problem with the first check on enrollment as it runs before the Mac reboots and enables FileVault. This gives us a non-compliance that shouldn't be there. At the moment I use a brute force method to fix this. I take out the FileVault rule and treat it separately. I am attempting to find a better solution, if you think of one I would love to hear it.
172 |
173 | ##### What About That Preference File?
174 |
175 | What file? Oh, *that* preference file. That is the file `org.cis_lvl2_puck.audit.plist`. Before we go too far let me point out that when you have the system all installed and running you will have **two** files with the same name. One in `/Library/Preferences` which holds the **output** of the compliance script and one in `/Library/ManagedPreferences` which will be the preference file **for** the compliance script.
176 |
177 | It is the second one we are talking about now.
178 |
179 | This preference file is for 'special cases'. It has a list of all the rules and a key `exempt` which is false unless we want to exempt that rule where we change the key to true. Here is what a rule looks like:
180 | ```
181 | sysprefs_automatic_login_disable
182 |
183 | exempt
184 |
185 |
186 | ```
187 |
188 | What if we had a number of Mac Minis used as an XCode build cluster. They're hiding in a server room without monitors or keyboards so it might be safe to power them up and log in automatically after a power outage. So we change that `` to `` and upload the preference file exactly the same way we did other config profiles. Scope that preference file to the build Minis and we have handled our 'special case'.
189 |
190 | This could get quite tedious and error prone. Fortunately Bob Gelder has written a script, `generate_json.py`, that will generate the JSON of a custom schema that can be uploaded to Jamf. You then end up with a config profile that allows you to easily turn rules on and off. You can find it at https://github.com/boberito/mscp_jamf/blob/main/generate_json.py. Put that in the `scripts` folder of the project and run it from the top of the project: `~/work/macos_security $ ./scripts/generate_json.py custom/cis_lvl2_puck_exempt.yaml`
191 |
192 | Notice that I'm pointing it not at my baseline but at a different YAML file. If you point it at your baseline you will get a lot of rules and a large JSON file (for me it was just over 60 rules and just over 1600 lines of JSON). I created a copy of the file with `_exempt` added to the name then went through the file and deleted any rule that I knew would never be exempted. That gave me a third of the rules and a third of the size for my JSON file. The downside is that you need to manually change the file name, and the title and description at the top of the file by removing the `_exempt`. Here's what that looks like in Jamf.
193 |
194 | 
195 | 
196 | 
197 |
198 | Notice how when we change a rule to "Configured" and set "Exempt" to "true" the interface adds a field for a reason to exempt the rule. Then further down you can see what a rule looks like before you do anything to it. Having this in the Jamf GUI is incredibly useful.
199 | #### Conclusion
200 |
201 | That's an explanation for how __I__ use the NIST macos_security project. I hope you found it useful for building _your_ security system. Keep an eye out for changes as I try to improve things. Feedback of any kind would be appreciated.
202 |
203 | Before I go I'd like to thank Stephen Corbiaux for field testing an early version of this HOWTO and Allen Golbig for correcting some errors in setup.
204 |
--------------------------------------------------------------------------------
/compliance.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Honestpuck/NIST-macos-security-HOWTO/456ce06992ce74a81aa54183981d3790260d079f/compliance.png
--------------------------------------------------------------------------------
/exempt1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Honestpuck/NIST-macos-security-HOWTO/456ce06992ce74a81aa54183981d3790260d079f/exempt1.png
--------------------------------------------------------------------------------
/exempt2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Honestpuck/NIST-macos-security-HOWTO/456ce06992ce74a81aa54183981d3790260d079f/exempt2.png
--------------------------------------------------------------------------------
/exempt3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Honestpuck/NIST-macos-security-HOWTO/456ce06992ce74a81aa54183981d3790260d079f/exempt3.png
--------------------------------------------------------------------------------
/policylist.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Honestpuck/NIST-macos-security-HOWTO/456ce06992ce74a81aa54183981d3790260d079f/policylist.png
--------------------------------------------------------------------------------
/smartgroup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Honestpuck/NIST-macos-security-HOWTO/456ce06992ce74a81aa54183981d3790260d079f/smartgroup.png
--------------------------------------------------------------------------------