A framework is a type of bundle that packages dynamic shared libraries with the resources that the library requires, including files (nibs and images), localized strings, header files, and maybe documentation. The .framework is an Apple structure that contains all of the files that make up a framework.

Frameworks are stored in the following location (where the * is the name of an app or framework):

  • /Applications/*contents/Frameworks
  • /Library/*/
  • /Library/Application Support/*/*.app/Contents/
  • /Library/Developer/CommandLineTools/
  • /Library/Developer/
  • /Library/Frameworks
  • /Library/Printers/
  • /System/iOSSupport/System/Library/PrivateFrameworks
  • /System/iOSSupport/System/Library/Frameworks
  • /System/Library/CoreServices
  • /System/Library/Frameworks
  • /System/Library/PrivateFrameworks
  • /usr/local/Frameworks 

If you just browse through these directories, you’ll see so many things you can use in apps. You can easily add an import followed by the name in your view controllers in Swift. For example, in /System/Library/Frameworks you’ll find the Foundation.framework. Foundation is pretty common as it contains a number of APIs such as NSObject (NSDate, NSString, and NSDateFormatter). 

You can import this into a script using the following line:

import Foundation

As with importing frameworks/modules/whatever (according to the language) – you can then consume the methods/variables/etc in your code (e.g.  let url = NSURL(fileURLWithPath: “names.plist”).

Super-Simple Bash Graphs

The sparkr gem is installed by default in macOS. To use it to produce simple graphs, simply run it followed by a series of integers:

sparkr 12 110 250 110 12

The result would be as follows:

This is useful for a quick and dirty visualization in scripts. For example, a series of 5, 10, 200 numbers that don’t have that much range where you’re just looking for a simple pattern. Like number of lines in logs, etc. Obviously, you can pay a lot of money for graphing frameworks and very fancy-schmancy tools. This is really just for me in small scripts. 

Note: sparkr isn’t installed on all Mac systems. to install it manually use:

sudo gem install sparkr

Thanks to Armin Briegel for pointing out that sparkr isn’t installed by default on the latest OSen.

Command Line Fu: Open Hidden Apps In macOS

macOS allows you to launch an app but in a hidden state. To do so, use the open command to open the app and then use the -a flag to specify the path of the app and –hide after the path to the app, as follows:

/usr/bin/open -a /Applications/ --hide

Quick and Dirty OpenBSM Auditing In macOS

OpenBSM is a subsystem that has been installed on the Mac for some time. OpenBSM provides that ability to create and read audit logs based on the Common Criteria standards.

Audit Logs

The quick and easy way to see what OpenBSM is auditing is to cat the /etc/security/audit_control file:

cat /etc/security/audit_control

The output displays the directory of audit logs, as well as what is currently being audited. By default the configuration is as follows:

# $P4: //depot/projects/trustedbsd/openbsm/etc/audit_control#8 $

You can then see all of the files in your audit log, using a standard ls of those 

ls /var/audit

As you can see, the files are then stored with a date/time stamp naming convention. 

20180119012009.crash_recovery 20180407065646.20180407065716 20180407073931.20180407074018
20180119022233.crash_recovery 20180407065716.20180407065747 20180407074018.20180407074050
20180119043338.crash_recovery 20180407065747.20180407065822 20180407074050.20180511030725
20180119134354.crash_recovery 20180407065822.20180407065853 20180511030725.crash_recovery
20180208172535.crash_recovery 20180407065853.20180407065928 20180616025641.crash_recovery
20180219133137.crash_recovery 20180407065928.20180407070004 20180624022028.crash_recovery
20180312153634.crash_recovery 20180407070004.20180407070036 20180718235941.crash_recovery
20180312160131.crash_recovery 20180407070036.20180407071722 20180720031150.crash_recovery
20180322141701.crash_recovery 20180407071722.20180407072215 20180724021901.crash_recovery
20180330190040.crash_recovery 20180407072215.20180407072259 20180728173033.crash_recovery
20180330191420.20180407064622 20180407072259.20180407073747 20180907031058.crash_recovery
20180407064622.20180407065616 20180407073747.20180407073836 20180911021141.not_terminated
20180407065616.20180407065646 20180407073836.20180407073931 current

The files are binary and so cannot be read properly without the use of a tool to interpret the output. In the next section we will review how to read the logs. 

Using praudit

Binary files aren’t easy to read. Using the praudit binary, you can dump audit logs into XML using the -x flag followed by the path of the log. For example, the following command would read a given log in the above /var/audit example directory:

praudit -x 20180407065747.20180407065822

One record of the output would look as follows

<record version="11" event="session start" modifier="0" time="Sat Apr 7 01:58:22 2018" msec=" + 28 msec" >
<argument arg-num="1" value="0x0" desc="sflags" />
<argument arg-num="2" value="0x0" desc="am_success" />
<argument arg-num="3" value="0x0" desc="am_failure" />
<subject audit-uid="-1" uid="root" gid="wheel" ruid="root" rgid="wheel" pid="0" sid="100645" tid="0" />
<return errval="success" retval="0" />

In the above output, you’ll find the time that an event was logged, as well as the type of event. This could be parsed for specific events, and, as an example, just dump the time and event in a simple json or xml for tracking in another tool. For example, if you’re doing statistical analysis for how many times privileges were escalated as a means of detecting a bad actor on a system.

You can also use the auditreduce command to filter records. Once filtered, results are still in binary and must be converted using praudit.

Move From Hosting Files From A macOS Server To A macOS Client

Migrating file services from a macOS Server to a macOS Client can be a bit traumatic at first. Mostly because the thought itself can be a bit daunting. But once you get started, it’s pretty simple. Mostly because there’s less to do. And that can be a challenge. While there are ways to hack together solutions for network homes and other more advanced features, if you’re doing that, then you’re missing a key point here. 

Let’s start by documenting our existing share points. We’ll do this with the serveradmin command and using the settings verb for the sharing service as follows:

sudo serveradmin settings sharing

Each share is an item in the sharePointList array, with the following:

sharing:sharePointList:_array_id:/Users/charles.edge/Public:nfsExportRecord = _empty_array sharing:sharePointList:_array_id:/Users/charles.edge/Public:smbName = “Charles Edge’s Public Folder” sharing:sharePointList:_array_id:/Users/charles.edge/Public:name = “Charles Edge’s Public Folder” sharing:sharePointList:_array_id:/Users/charles.edge/Public:afpIsGuestAccessEnabled = yes sharing:sharePointList:_array_id:/Users/charles.edge/Public:isIndexingEnabled = no sharing:sharePointList:_array_id:/Users/charles.edge/Public:dsAttrTypeNative\:sharepoint_group_id = “6C37A421-C506-4523-8769-1AF6EA245B68” sharing:sharePointList:_array_id:/Users/charles.edge/Public:mountedOnPath = “/” sharing:sharePointList:_array_id:/Users/charles.edge/Public:dsAttrTypeNative\:sharepoint_account_uuid = “C0405AE4-6CBE-40C7-9584-174687C80C07” sharing:sharePointList:_array_id:/Users/charles.edge/Public:path = “/Users/charles.edge/Public” sharing:sharePointList:_array_id:/Users/charles.edge/Public:smbIsShared = yes sharing:sharePointList:_array_id:/Users/charles.edge/Public:smbIsGuestAccessEnabled = yes sharing:sharePointList:_array_id:/Users/charles.edge/Public:afpName = “Charles Edge’s Public Folder” sharing:sharePointList:_array_id:/Users/charles.edge/Public:dsAttrTypeStandard\:GeneratedUID = “5C13E2AA-A86D-45D0-80B4-00CA86DE2253” sharing:sharePointList:_array_id:/Users/charles.edge/Public:smbDirectoryMask = “755” sharing:sharePointList:_array_id:/Users/charles.edge/Public:afpIsShared = yes sharing:sharePointList:_array_id:/Users/charles.edge/Public:smbCreateMask = “644” sharing:sharePointList:_array_id:/Users/charles.edge/Public:ftpName = “Charles Edge’s Public Folder”

Once you’ve removed the Server app, you’ll be left with using the sharing command. Using that command, you can list shares using the -l option:

sharing -l

That same share then appears as follows:

List of Share Points
name: Charles Edge’s Public Folder
path: /Users/charles.edge/Public
afp: {
name: Charles Edge’s Public Folder
shared: 1
guest access: 1
inherit perms: 0
smb: {
name: Charles Edge’s Public Folder
shared: 1
guest access: 1
read-only: 0
sealed: 0

Or from the Sharing System Preference Pane.

Now you just have to loop through and create each share (although they should co-exist between tools). To create a share, click on the plus sign under Shared Folders.

You can then browse to the folder you’d like to share. Next, we’ll give access to the directory. Use the plus sign on the right side of the screen and then select the user or group you’d like to add to the list that has access to the directory (while the directory is highlighted in the list on the left).

Once the user is in the list, use the permissions on the right side of the user list to select what level each user or group gets.

You have additional controls for file and folder security that can be set at either the directory that is shared or those below it hierarchically. To do so, highlight the directory and use the Get Info option under the File menu in the Finder.

Note: You can also check the Shared Folder box on these folders to share them, meaning you have one less step once you get used to the workflow!

Backup Macs with Carbonite

Carbonite is a great tool for backing up Macs and Windows devices. To install Carbonite, download it from Once downloaded, copy the app to the /Applications directory and open the app. 

The Carbonite app will then install the components required to support the backup operations and index the drive.

Next, you’ll see some basic folders that will be backed up. Check the box for those you want to add to the backup (or do this later) and click the Install button.
Click Open Carbonite.

Notice that the backup has begun! The only really customer-installable action is to select the directories to be backed up, which is done using the left-hand sidebar. 

And that’s it. There aren’t a lot of other options in the GUI. You can access more options at /Library/Preferences/com.carbonite.carbonite.plist. 

DNS: Install BIND on macOS

The DNS service in macOS Server was simple to setup and manage. It’s a bit more manual in macOS without macOS Server. The underlying service that provides DNS is Bind. Bind will require a compiler to install, so first make sure you have the Xcode command line tools installed. To download Bind, go to ISC at From there, copy the installer locally and extract the tar file. Once that’s extracted, run the configure from within the extracted directory:

./configure --enable-symtable=none --infodir="/usr/share/info" --sysconfdir="/etc" --localstatedir="/var" --enable-atomic="no" --with-gssapi=yes --with-libxml2=no

Next, run make:


Then run make install:

make install

Now download a LaunchDaemon plist (I just stole this from the org.isc.named.plist on a macOS Server, which can be found at /Applications/ or downloaded using that link). The permissions for a custom LaunchDaemon need to be set appropriately:

chmod root:wheel /Library/LaunchDaemons/org.isc.named.plist

Then start it up and test it!

launchctl load -w /Library/LaunchDaemons/org.isc.named.plist

Now you can manage the server as we described at

Migrate From macOS To A Synology Based VPN

Synology is able to do everything a macOS Server could do, and more. So if you need to move your VPN service, it’s worth looking at a number of different solutions. The most important question to ask is whether you actually need a VPN any more. If you have git, mail/groupware, or file services that require remote access then you might want to consider moving these into a hosted environment somewhere. But if you need access to the LAN and you’re a small business without other servers, a Synology can be a great place to host your VPN services. 

Before you setup anything new, first snapshot your old settings. Let’s grab  which protocols are enabled, running the following from Terminal:

sudo serveradmin settings

sudo serveradmin settings

Next, we’ll get the the IP ranges used so we can mimic those (or change them) in the new service:

sudo serveradmin settings

Now let’s grab the DNS servers handed out so those can be recreated:

sudo serveradmin settings
sudo serveradmin settings

Finally, if you’re using L2TP, let’s grab the shared secret:

sudo serveradmin settings

Once we have all of this information, we can configure the new server using the same settings. To install the VPN service on a Synology, first open the Synology and click on Package Center. From there, click on All and search for VPN.

Then click on the Install button for VPN. Once installed, open VPN Server from the application launcher in the upper left-hand corner of the screen. Initially, you’ll see a list of the services that can be run, which include the familiar PPTP and L2TP, along with the addition of Open VPN.

Before we potentially open up dangerous services to users we might not want to have access to, click on Privilege. Here, enable each service for each user that you want to have access to the VPN services.

Now that we can safely enable and disable each of the services, click on PPTP in the sidebar of the VPN Server app (if you want to provide PPTP-based services to clients).

Here, check the box for “Enable PPTP VPN server” and enter the following information:
  • Dynamic IP address: The first DHCP address that will be given to client computers
  • Maximum connection number: How many addresses that can be handed out (and therefore the maximum number of clients that can connect via PPTP).
  • Maximum number of connections with the same account: How many sessions a given account can have (1 is usually a good number here).
  • Authentication: Best to leave this at MS-CHAP v2 for compatibility, unless you find otherwise.  
  • Encryption: Leave as MPPE optional unless all clients can do MPPE and then you can enforce it for a stronger level of encryption.
  • MTU: 1400 is a good number.
  • Use manual DNS: If clients will connect to services via names once connected to the VPN, I’d put your primary DNS server in this field.

Click Apply and open port 1723 so clients can connect to the service. If you’ll be using L2TP over IPSec, click on “L2TP/IPSec” in the sidebar. The settings are the same as those above, but you can also add a preshared key to the mix. Go ahead and check the enable checkbox, provide the necessary settings from the PPTP list, and provide that key and then click on Apply. Note that the DHCP pools are different between the two services. Point UDP ports 1701, 500, and 4500 at the new server to allow for remote connections and then test that clients can connect.

That’s it. You’ve managed to get a new VPN setup and configured. Provided you used the same IP address, same client secret, and the ports are the same, you’ll then be able to probably use the same profile to install clients that you were using previously.