Hello 👋 I'm Dan.

I do Cloud DevOps and write PHP. I also care about web performance and security.

OS X Mavericks Server - Setting Up Contacts

The Contact service in OS X Mavericks Server is a great way to easily sync contacts between users and groups. Along with the Calendar service, it has deep ties with the Apple Push Notification Service (APNS) to push all new changes to your devices.

Contents\

  • Pro-Tip: SRV Record For Easy Configuration
  • Setting Up In Contacts.app Using CardDAV

What you may not know is that the Contact service is actually tied into the Calendar service. If you enter serveradmin settings calendar and serveradmin settings addressbook you’ll find that they match line for line (with exception to the name of course). You may also notice that there’s not really much that can be configured for Contacts (annoyingly, Contacts is still called Address Book in the serveradmin backend), pretty much the only thing you can change in the GUI are settings for APNS and whether Address Book is allowed to query the Directory Service.

Open up the Server app and click on the Contacts section so we can start it up! Click the On button to start the Contacts server! If you enter tail -f /var/log/caldavd/servermgr_calendar.log in Terminal before you turn Contacts on, you’ll see the config changes get pushed, then the collaboration services will restart.

Now that Contacts is running, you’re pretty much done! You can optionally allow contacts to be shared via LDAP, but that’s not a requirement. Typically you won’t need to enable this.

Pro-Tip: SRV Record For Easy Configuration

Along with Calendar, you can set up a SRV DNS record for the CardDAV TCP protocol. Essentially, it’s like an MX record, but for other protocols. We can utilise the DNS service in OS X Mavericks Server to create this SRV record.

If you open up the Server app and go to the DNS section, I’ll show you how to create a SRV record. Select your Primary Zone, then hit the + (plus) in the bottom-left corner, then click “Add Service Record…”. This will bring up a window overlay with some empty fields.

In these tutorials we’ve only been working with a primary zone, so we won’t change it. Actually, you can’t even change it (because there’s only one zone), so don’t even bother trying. The primary zone should be your domain name.

Next, for Service Name, leave this blank as we aren’t using any Bonjour browser goodness. Finally, the first field we actually enter something in is Service Type. Because we’re using the SSL version of the CardDAV protocol over TCP, we will enter _carddavs._tcp. If you don’t want to use SSL (why?), enter _carddav._tcp.

For the host name, enter the FQDN (Fully Qualified Domain Name) of the server you want to be your CardDAV server. Given the fact that in these examples we have only been using one server, just enter the host name for your server. Mine is mavericks.pretendco.com. The next field is Port, which defines what port this service is running on. Because we’re using SSL, we’ll enter in 8443. If you weren’t using SSL, you’d enter 8008.

If you’re unsure what port your Contact server is using, enter the command below to get your port. Note the command to the right of either command, use the top one if you use SSL, use the bottom command if you don’t.

sudo serveradmin settings addressbook:SSLPort # Using SSL
sudo serveradmin settings addressbook:HTTPPort # Not Using SSL :(

If you really have no idea if you’re using SSL or not, you can enter sudo serveradmin settings addressbook:RedirectHTTPToHTTPS to find out if you are. If the answer is yes, then you’re using SSL.

Okay, that’s enough ranting about ports. The last two important fields are Priority and Weight. As I’ve said a few times earlier, we’re only using one server in these examples, so enter 0 for Priority and 1 for Weight. The final field is Text, we are leaving it blank. Some services may need some information in the Text field, but CalDAV/CardDAV don’t.

For a more readable list, see below:

  • Zone: pretendco.com
  • Service Name: Leave Blank!
  • Service Type: _carddavs._tcp
  • Host Name: mavericks.pretendco.com
  • Port: 8443
  • Priority: 0
  • Weight: 1
  • Text: Leave Blank!

All in all, you should have something that looks like this:

The CardDAV SRV Record.
The CardDAV SRV Record.

Now that you’re done, click Create to create the SRV record. If all goes well, you should be taken back to your DNS Records list, and your new SRV record should be in that list. If not, you probably did something wrong (hint: maybe you put a space in the Service Name field). To verify that the SRV record has been added, enter the following command in Terminal on the server:

dig SRV _carddavs._tcp.pretendco.com

If all goes well, you should see something like this:

; <<>> DiG 9.8.3-P1 <<>> SRV _carddavs._tcp.pretendco.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10499
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1

;; QUESTION SECTION:
;_carddavs._tcp.pretendco.com.	IN	SRV

;; ANSWER SECTION:
_carddavs._tcp.pretendco.com. 10800 IN	SRV	0 1 8443 mavericks.pretendco.com.

;; AUTHORITY SECTION:
pretendco.com.		10800	IN	NS	mavericks.pretendco.com.

;; ADDITIONAL SECTION:
mavericks.pretendco.com. 10800	IN	A	10.1.125.120

;; Query time: 4 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Sat Oct 19 22:27:32 2013
;; MSG SIZE  rcvd: 119

Hooray, everything is OK.

Setting Up In Contacts.app Using CardDAV

So, you’ve got the Contacts service up and running, and now you want to add it to the Contacts app in OS X Mavericks so your users can start adding contacts to their contact list. Open up the Contacts app then go to Preferences then click on the Accounts pane.

Contacts: Add Other Contacts Account.
Contacts: Add Other Contacts Account.

Hit the + (plus) in the bottom-left corner to add an account. Because we’re using our own contact system, use the option at the very bottom called “Other contacts account…” then click Continue. Sadly, Contacts still isn’t as nice as Calendar when setting up a custom CardDAV account, you don’t get the nice automatic/manual/advanced options. All you get is CardDAV or LDAP. Given that we’re using CardDAV, ensure that CardDAV is selected.

Contacts: CardDAV User Details.
Contacts: CardDAV User Details.

In the User Name field, enter your username. I’m going with Jim Halpert, so I’ll enter in jhalpert. Next up is the Password, and Jim’s password is pam.

Finally, we enter the Server Address. If you did the SRV record step about, you only have to enter your domain name! In my case, it’s pretendco.com. If not, enter the FQDN for your server (mavericks.pretendco.com). If you try and set up the user using the SRV method and you haven’t made the SRV record, you’ll get an error about not being able to make a secure connection. If all is successful, click Create to create the user.

Note: In my testing, I’ve noticed that on OS X Mavericks when adding the CardDAV account Contacts.app freezes for a couple of seconds before adding the account, or it freezes until killed. Just take that into account when adding your account!

OS X Mavericks Server - Setting Up Calendaring

The Calendar service in OS X Mavericks Server is a really easy way to get collaboration between users set up, along with allowing them to allocate resources and locations within your business to events. With deep ties in the Apple Push Notification Service (APNS) and Mail services, notifying people about your events is quick and easy.

Contents

  • Getting Started
  • Pro-Tip: SRV Record For Easy Configuration
  • Email Invitations
  • Resources & Locations

Getting Started

First up, let’s actually get Calendaring up and running! Open the Server app and click on the Calendar section. Hit the little On button to start it up!

When turning on Calendar for the first time, or when you’re upgrading, it’s worth taking a look at a few logs to get an idea of what’s happening in the backend. Getting in the habit of reading logs is a great way to learn, and of course, troubleshoot. All of the Calendaring logs are stored in /var/log/caldavd/, but the most interesting logs at this stage will be /var/log/caldavd/servermgr_calendar.log, /var/log/caldavd/migration.log and /var/log/caldavd/error.log. Once the Calendar server has started we’re ready to rock and/or roll.

The Calendar service is now running!
The Calendar service is now running!

Pro-Tip: SRV Record For Easy Configuration

If you are hosting DNS on your server, or have full access to creating DNS records with your hosting provider, you can add SRV records that can make it that little bit quicker to add Calendar accounts for all your users. From here on, I’m assuming you’re creating the DNS record in the Server app.

Go to the DNS section of the Server app, and click the + (plus) in the bottom-left corner then click on “Add Service Record…”

Because we only have one DNS zone (aka, the Primary Zone), leave the Zone field alone (if you’ve only got one zone, you can’t even change it). Leave the Service Name field blank as it is for an optional Bonjour browsing field, which we aren’t using.

Next up, we enter the Service Type. The Server app thinks it can be helpful by offering you a few suggestions, but the CalDAV service isn’t listed there. We need to add it. In the Service Type field, enter _caldavs._tcp. If you’re not using SSL, remove the s from caldavs.

For Host Name, enter your hostname. I will be entering mavericks.pretendco.com. As we are using SSL, we’ll put in 8443 as that is the default SSL port for CalDAV. If for some reason you decided you aren’t using SSL, you should enter 8008 as the port instead.

If you’re using multiple servers for collaboration, you can specify different priority and weight numbers. In a normal environment with one server, we will enter 0 for priority, and 1 for weight, as they are the defaults. Leave Text blank, we don’t need it.

For a more readable list, see below:

  • Zone: pretendco.com
  • Service Name: Leave Blank!
  • Service Type: _caldavs._tcp
  • Host Name: mavericks.pretendco.com
  • Port: 8443
  • Priority: 0
  • Weight: 1
  • Text: Leave Blank!

You should now see something like this:

The CalDAV SRV Record.
The CalDAV SRV Record.

To double-check this is working, enter the following command in Terminal:

dig SRV _caldavs._tcp.pretendco.com

With any hope, you should see the following result:

; <<>> DiG 9.8.3-P1 <<>> SRV _caldavs._tcp.pretendco.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 21649
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1

;; QUESTION SECTION:
;_caldavs._tcp.pretendco.com.	IN	SRV

;; ANSWER SECTION:
_caldavs._tcp.pretendco.com. 10800 IN	SRV	0 1 8443 mavericks.pretendco.com.

;; AUTHORITY SECTION:
pretendco.com.		10800	IN	NS	mavericks.pretendco.com.

;; ADDITIONAL SECTION:
mavericks.pretendco.com. 10800	IN	A	10.1.125.120

;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Sat Oct 19 16:28:47 2013
;; MSG SIZE  rcvd: 118

Hooray! Your SRV record is now working correctly. Go over to the Calendar app and we can set up a user account. Open Preferences in the Calendar app and jump over to the Accounts section, then click the + (plus) to add a new account

Add a New Account in the Calendar app.
Add a New Account in the Calendar app.

Now, choose the option “Add CalDAV Account…” and then click Continue.

Add Our User Using the "Automatic" Method.
Add Our User Using the "Automatic" Method.

Because we set up the SRV record, we don’t have to choose the “Manual” or “Advanced” options. Type in your email address and password, then click Create. For example, the user Jim Halpert that we set up earlier, we would enter the details below:

  • Email Address: jhalpert@pretendco.com
  • Password: pam

Once you’ve clicked Create, you should be taken back to the Accounts pane of Preferences. The first thing I change is the Description to the first name of the user. For example, I’ll change it to Jim. The only other option you might want to change is the Availability field.

Account Information for the User Jim Halpert.
Account Information for the User Jim Halpert.

Email Invitations

First up, we’ll configure our Email user that will be able to send and receive calendar invitations. To do this, check the box next to “Enable invitations by email”. This will run you through a few steps of configuration, most of which you shouldn’t have to change.

Set Up A Calendar Email User.
Set Up A Calendar Email User.

The first setting to review is the email address of the Calendar user. Normally, I would leave this as com.apple.calendarserver@mavericks.pretendco.com, but you can change it to another user (if you’ve already added them into the Open Directory database). It would be wise to leave it alone.

Enter the Calendar Email User IMAP Details.
Enter the Calendar Email User IMAP Details.

Next up is configuring the incoming mail server settings for the Calendar email user. If the server you’re configuring is the same server as your Mail server, you won’t have to change a thing. Otherwise, you’ll have to enter your Mail server settings for IMAP (or POP, but please tell me you’re using IMAP!). Note that the email address you chose in the first step should match the user name for authentication.

Enter the Calendar Email User SMTP Details.
Enter the Calendar Email User SMTP Details.

Now it’s time to configure the SMTP server settings for sending Calendar invitations. Like above, you may not have to change any settings at all, but if you’re using another Mail server, enter the configuration details as applicable.

Double-Check Your Email User Settings.
Double-Check Your Email User Settings.

Finally, ensure all your settings are correct before clicking Finish. Once you click Finish, the Calendar server will restart to save those changes. Now you’re all done configuring mail for the Calendar service. For the curious, enter sudo serveradmin settings calendar | grep "iMIP" to see all the settings for the sending and receiving of Calendar invitations by email.

If you create a sample event then invite yourself using an email address, you’ll get an email invitation that has some interesting information that you can look at.

You’ll notice that the reply-to email address is in the format calendar-email-user+event-guid@domain.com. For example, a sample event I did had the reply-to email address com.apple.calendarserver+35f7b7c1-1295-4513-a9b7-c859ec3a7d94@mavericks.pretendco.com. This uses the concept of email address tagging whereby you have the local part of the email address (com.apple.calendarserver, mscott, etc) then you add a + (plus) and enter the tag. What this does is allows the mail server to do is, using a combination of rules, direct email flow depending on what tag it has. A few modern mail providers (like Gmail) support tagging. Essentially, if I replied to the email address com.apple.calendarserver+35f7b7c1-1295-4513-a9b7-c859ec3a7d94@mavericks.pretendco.com, the inbuilt mail server would know that this email is related to the event that has a GUID of 35f7b7c1-1295-4513-a9b7-c859ec3a7d94. Pretty clever huh?

Another neat’o trick is when you invite someone who isn’t in Open Directory domain they’ll get an email with the event details, along with an .ics that they can add to their own calendar! For those curious about what an .ics (or iCalendar file) contains, take a look below:

BEGIN:VCALENDAR
CALSCALE:GREGORIAN
VERSION:2.0
X-WR-CALNAME:Board Meeting
METHOD:PUBLISH
PRODID:-//Apple Inc.//Mac OS X 10.9//EN
BEGIN:VTIMEZONE
TZID:Australia/Melbourne
BEGIN:STANDARD
TZOFFSETFROM:+1100
RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU
DTSTART:20080406T030000
TZNAME:AEST
TZOFFSETTO:+1000
END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:+1000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU
DTSTART:20081005T020000
TZNAME:AEDT
TZOFFSETTO:+1100
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
TRANSP:OPAQUE
DTEND;TZID=Australia/Melbourne:20131022T110000
X-APPLE-STRUCTURED-LOCATION;VALUE=URI;X-TITLE="Meeting Room 1, 13927 Saticoy Street, Panorama City, CA 91402, USA":
ORGANIZER;CN="Michael Scott";EMAIL="mscott@pretendco.com":urn:uuid:1C3F7FD2-2ECA-4F5E-A332-458D59BF3EAE
UID:4E773374-E5C3-4F66-9E71-7F151D31E7B2
DTSTAMP:20131019T003921Z
LOCATION:Meeting Room 1\, 13927 Saticoy Street\, Panorama City\, CA 91402\, USA
DESCRIPTION:Be sure to bring the monthly reports.
SEQUENCE:12
X-CALENDARSERVER-ATTENDEE-COMMENT;X-CALENDARSERVER-ATTENDEE-REF="urn:uuid:B6103F55-0E50-4254-80B45CFF73E32B8E";X-CALENDARSERVER-DTSTAMP=2013101 9T003950Z:Will bring this months estimates too!
SUMMARY:Board Meeting
DTSTART;TZID=Australia/Melbourne:20131022T100000
CREATED:20131018T225816Z
ATTENDEE;CN="Jim Halpert";CUTYPE=INDIVIDUAL;EMAIL="jhalpert@pretendco.com";PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE;SCHEDULE-STATUS="1.2":urn:uuid:7FD59E48-6836-4AC4-9674-284B1FC383BD
ATTENDEE;CN="Michael Scott";CUTYPE=INDIVIDUAL;EMAIL="mscott@pretendco.com";PARTSTAT=ACCEPTED:urn:uuid:1C3F7FD2-2ECA-4F5E-A332-458D59BF3EAE
ATTENDEE;CN="Pam Halpert";CUTYPE=INDIVIDUAL;EMAIL="phalpert@pretendco.com";PARTSTAT=ACCEPTED;ROLE=REQ-PARTICIPANT;SCHEDULE-STATUS="2.0":urn:uuid:B6103F55-0E50-4254-80B4-5CFF73E32B8E
END:VEVENT
END:VCALENDAR

And here is the event in Calendar:

The Board Meeting event as it appears in the Calendar app.
The Board Meeting event as it appears in the Calendar app.

Resources & Locations

As with almost any other calendaring solution, OS X Mavericks Server has the ability to define unique resources and locations. These resources and locations are defined as invitees so you can allocate them events as, you guessed it, resources and locations.

For example, say the company has a car that can be used by employees for onsite visits or meetings. For them to use the car, they first have to allocate it to their event. If the car is already being used by someone else, they will be told when adding the car that it is not available, otherwise, they’ll be able to add the car to the event and have it assigned to them. First, we need to create the car resource!

Resource: PretendCo Car
Resource: PretendCo Car

In the “Locations and Resources” section of the Calendar pane in the Server app you’ll see a + (plus) in the bottom-left corner. Click it to create a new Location or Resource. Since the car is obviously a resource and not a location, we’ll make the Type a “Resource”.

Next, we need to give the resource a unique name. Given that PretendCo only has one car, we’ll call the resource “PretendCo Car”. We’re going to trust our employees enough to be able to organise the car amongst themselves so we won’t require the resource to be approved by a delegate.

Finally, with Delegate, I’m going to leave it blank. You can however enter a username or groupname that can both view and manage the resource (or location). Even though you have automatically accept requests for this resource, delegates can override that status to manually deny a request.

Click Create to create your first resource! You can now assign this resource in an event by entering “PretendCo Car” as an invitee. Give it a go, you’ll see that the PretendCo car is automatically approved.

Next up is Locations. Locations, like Resources, give you the ability to define specific locations that are applicable for your server, and allow people to assign locations to their events. Along with resources, these can be reserved, and give users that are trying to organise times for locations the ability to check their availability.

Location: Meeting Room 1
Location: Meeting Room 1

Like we did with resources, click the plus in the bottom-left corner to create a new location. Make sure to change the Type to Location in the drop down menu. You need to ensure that locations are actually added as locations, because you add them in a different place when creating an event.

Next we give it a name. I’m going to go with “Meeting Room 1” because it’s simple. Because this is a meeting room, I’m going to make the Management group approve every request to use the room. Why? Because I can. And it’s a good reason to talk about delegation. With that in mind, change Accepted Invitations to “With Delegate Approval” in the drop down menu.

In the Delegate field you can enter either a username, or a group name if you’d like a specific group to manage delegation. For example, if you have one person in the company you trust to manage delegation, enter that person’s username into the field. Otherwise, you may have a group of people that manage delegation, so enter their group name in.

Finally, for Accept Group, you can enter the name of a group that this location will automatically accept requests from, even if you require invitations to be accepted with delegate approval. Typically, you would probably add your Management group to that list. For PretendCo, I did.

Click Create and you have now created your first location. Open up the Calendar app and create an event. The field down from the event title is location. Start typing in Meeting Room 1, Calendar should predict you’re going to invite Meeting Room 1, so click on it (or press Return). Note that if you assign a location that’s in the Open Directory domain, you can add a secondary location, i.e. the address of said Meeting Room 1. See my event below for an example of two locations being assigned for 1 event.

Calendar Event with a Location and Address.
Calendar Event with a Location and Address.

OS X Mavericks Server - Adding New Users

Adding users to your server is probably going to be something you will end up doing the most once the server has been configured, so it’s important that you’re doing right. Personally I’ve found to be the most valuable tip when creating users is to… keep it consistent! I’ve been brought into far too many environments when the shortnames, email addresses and even names are inconsistent. Personally, it drives me nuts and can cause confusion between staff about what their username is.

Contents

  • SACLs
  • User Templates

Before we start, here’s my style guide:

  • Name: Firstname Surname
  • Shortname: fsurname
  • Email: fsurname@pretendco.com

You’d think it’s pretty basic and obvious, but you’d be surprised how many people forgo style and consistency. Now, how about we get to creating users.

Open the Server app and go to the “Users” tab. If you’re using Open Directory (which you should be!), ensure you’re looking at “Local Network Users”. You’d hate to add users to the server, only to find that you’ve added them to the local domain, rather than the Local Network Domain (Your Open Directory domain). So far, you should only see the Directory Administrator user. Let’s change that. Click the + (plus) in the bottom-left corner to add a user!

  • Full Name: the full name of the user you’re adding. This is required, but does not have to be unique. While your Mac will require unique Full Names, Open Directory will allow you to have multiple users with the same Full Name.
  • Short Name: the shortname of the user you’re adding. This is also required, and must be unique. You will not be able to add more than one user with the same shortname, so if you have two people with the shortname jhalpert, consider having a counter at the end of the shortname, e.g. jhalpert, jhalpert2, jhalpert3 and so on. I opt to have the first user with no number because you may not always know if someone with the same shortname will be added the server, so it just looks neater to have no numbers on the end of the first shortname.
  • Email: the email for the user. This field isn’t required, and you can use it even if your server isn’t running Mail services. It’s purely cosmetic, and is used for the global address list in the Open Directory domain. Note: if you do use this field, but enter a different username and are running Mail on the server you must add an alias for that shortname, if you have jhalpert as the user shortname, but jim as the email username, Jim won’t receive email to jim until that name has been entered as an alias (coming up soon).
  • Password & Verify: enter the users password twice. Best to generate a password, save it to a Numbers document then distribute the password to each user. Otherwise, set a generic one then require them to change password on next login.
  • Allow user to administer this server: having this checked enables the user to successfully authenticate and have full authorisation for the Server app. Use it wisely.
  • Home Folder: I absolutely despise networked home folders, they screw up so often and are very hard to fix (sometimes impossible to fix). I will always change this to None - Services Only. Takes up less space on the server too, as it doesn’t generate a home folder in /Users.
  • Keywords: Add keywords for your user. This field is not required, and acts as a tag field for the user. It’s new to the GUI for Mavericks Server. I have yet to see a solid use case for this field, but I’m sure someone is using it to the full effect.
  • Notes: Add notes for the user. Like keywords, this field is optional, and again, I’ve yet to see a good use for this field.

Given that I love the hit TV show The Office (US) (P.S. I really love the UK original too), I’m going to use fake users that are named after main characters from the show. My first user will be Jim Halpert. Following the style guide as above, you’ll be entering something like this:

  • Full Name: Jim Halpert
  • Account Name: jhalpert
  • Email: jhalpert@pretendco.com
  • Password: a random password of your choice (for this example, I just did pam)
  • Home Folder: None - Services Only
  • Keywords: sales
  • Notes: Often plays pranks on Dwight.

Note: In reality, keywords and notes aren’t important to the functionality of OS X Server and Open Directory, it can help with usability, however. Note that adding the keyword sales won’t add them to a group called sales. Keywords are purely for easy searching.

Adding a New User: Jim Halpert.
Adding a New User: Jim Halpert.

I’ve also added a picture for Jim, I suspect most people don’t realise you can drag and drop and image file onto the generic user picture to add a picture for that person.

Once you’ve added those details, click “Create” to add the user! If there’s any errors, you’ll be notified and the window won’t change, otherwise, you should be redirected back to the Users list (with your new user in the list!)

SACLs

A handy feature in the Server app is the ability to restrict specific users to specific services. For example, you might say that newer employees are not allowed access to the VPN service, or maybe you want a shared account that only has access to File Sharing or collaboration services.

First off, what is a SACL? SACL stands for Service Access Control List, which, as the name would suggest, are lists that define user and group access to each service in OS X Mavericks Server. The lists themselves aren’t necessarily files in OS X, they’re memberships to specific groups in the local directory. For example, the service Contacts is actually a membership to the group com.apple.access_addressbook.

The SACL Groups in OS X Server.
The SACL Groups in OS X Server.

By default, all users are allowed access to all services. To change a specific users access to a group, you can do one of two things. The first option is to right click on the user (in the Users list) and then click “Edit Access to Services…”. You’ll be shown a list of the most common services (note: not all the services), where you can uncheck each box you don’t want the user to have access too.

Alternatively, you can go to the Groups section of the Server app and change the membership there. First off, you’ll have to enable to option to see system accounts, as they are hidden by default. Go up to View, then click on “Show System Accounts”. Now you’ll see a huge list of accounts, lots with interesting names. Scroll down until you get to the groups starting with com.apple.access_. If you open up those groups, you can add and remove members from there. Otherwise, you can go to the user in the Users section of the Server app and add or remove group memberships from there.

User Templates

New in OS X Mavericks Server is the ability to add users that conform to a specified template. You might decide that most users only need access to AFP, Contacts, Calendars, and Mail. You also decide that they won’t have a home folder (services only) on the server and cannot administer the server. To make a template, go to the Users section in Server app, then click the gear in the bottom-left corner and click on the “Edit Templates…” button.

The User Template window with no templates.
The User Template window with no templates.

Hit the plus to create a new template! I’m going to call mine Standard Sales User as I want to use it for all standard sales people.

Our Standard Sales User Template.
Our Standard Sales User Template.

Next, I’m going to ensure that “administer this server” is unticked, because standard sales users shouldn’t be allowed to modify the server in any way! Next, change home folder to “None - Services Only”. Then, change Login shell to “/bin/bash/false” (the default for OS X Mavericks Server).

Leave the Primary Group as staff, you don’t want to mess with that as the user may never be able to log in. Next up, with groups we can utilise what we learnt with SACLs to restrict what groups (and services) they have access too. As with all users, I’ll put them in the default “Workgroup” group, but we’ll also add them to a few other groups. As I mentioned, I want to restrict these users to only having access to several services. These services are AFP, Contacts, Calendars and Mail. Click the + (plus) for adding groups, and enter the following groups: com.apple.access_afp, com.apple.access_addressbook, com.apple.access_calendar and com.apple.access_mail.

If you want to add or remove any other services, refer to the list below which has the service name and group name for the Services in OS X Mavericks Server.

  • Contactscom.apple.access_addressbook
  • AFPcom.apple.access_afp
  • Time Machinecom.apple.access_backup
  • Calendarcom.apple.access_calendar
  • iMessagecom.apple.access_chat
  • Profile Managercom.apple.access_devicemanagement
  • FTPcom.apple.access_ftp
  • Mailcom.apple.access_mail
  • RADIUScom.apple.access_radius
  • SMBcom.apple.access_smb
  • VPNcom.apple.access_vpn

Finally, you can add keywords and notes for the user template. I’m just going to add sales for a keyword, and that’s it. Finally, click “Done” to add the template. Now to test out the template!

You’ll now notice that when you go to add a new user, at the top of the form you’ll have a new field called “Template” that has a drop-down menu of all your templates! I’m going to enter a fake account for a user called John Citizen. Check the image below for my configuration:

User John with Standard Sales User Template.
User John with Standard Sales User Template.

Now, click “Create” to create the user from the specified “Standard Sales User” template. Once the user has been created, right-click on the user then click “Edit Access to Services…”. This will bring a window that shows you all the services John Citizen is a member for. As you’ll see, all the services we entered above are ticked (with exception for File Sharing, as that combines AFP and SMB).

John and his Services.
John and his Services.

Excellent, John has the right privileges and is associated with the right groups, all thanks to our user template! I believe at this stage the User Template system is still in its infancy, but could grow to become a really powerful tool. I’d love to see the ability to customise the way it grabs the full name and brings that across to short name and email address.

OS X Mavericks Server - Setting Up Mail

So far in these posts you should have set up your OS X Mavericks Server, DNS, and set up an Open Directory Master, but now you’d like to set up Mail so you communicate with other people. Luckily, the initial set up of Mail is pretty easy, but there can be a few gotchas.

Contents

  • Extra Configuration Options
  • Virtual Domains

Firstly, before setting up Mail you must make sure you can connect to the internet. This may sound like a strange requirement, but if you start Mail for the first time and you’re not connected to the internet, Mail won’t start because ClamAV won’t start because it can’t download the latest anti-virus definitions and will time out. When testing, Mail wouldn’t start for 10 minutes before I gave up because ClamAV and Amavis couldn’t connect to the internet and would not start, thus causing the rest of the Mail services to throttle respawn and re-launch every 6-10 seconds. Fun!

Before continuing, make sure you’ve completed everything in the server set up, DNS configuration, and Open Directory posts to ensure your DNS and server is healthy.

OK, we’re now ready to click that big On button in Server app to start Mail, but before we do, open up Console, then under /Library/Logs, expand the Mail disclosure arrow and click on mail-info.log (or tail -f /Library/Logs/Mail/mail-info.log) to check out what happens in the backend when you start Mail. There’s also a few other logs you can take a gander at when starting the service, check them out in that same Mail section in Console.

Mail, The Service is now Running.
Mail, The Service is now Running.

Within a few minutes you should see that the status for Mail has changed to “Available at <your domain>”. As with Open Directory (and all other standard OS X Server services), you can query the basic status of Mail by entering the following:

sudo serveradmin status mail

And you’re receive the rather generic response (at least it’s concise):

mail:state = "RUNNING"

Now, if you’d like some more information regarding the status of Mail, its log paths, and which of the Mail services are running, you can enter the following:

sudo serveradmin fullstatus mail

And you’ll get something like this:

mail:startedTime = "2013-10-08 08:11:34 +0000"
mail:setStateVersion = 1
mail:state = "RUNNING"
mail:protocolsArray:_array_index:0:status = "ON"
mail:protocolsArray:_array_index:0:kind = "INCOMING"
mail:protocolsArray:_array_index:0:protocol = "IMAP"
mail:protocolsArray:_array_index:0:state = "RUNNING"
mail:protocolsArray:_array_index:0:service = "MailAccess"
mail:protocolsArray:_array_index:0:error = ""
mail:protocolsArray:_array_index:1:status = "ON"
mail:protocolsArray:_array_index:1:kind = "INCOMING"
mail:protocolsArray:_array_index:1:protocol = "POP3"
mail:protocolsArray:_array_index:1:state = "RUNNING"
mail:protocolsArray:_array_index:1:service = "MailAccess"
mail:protocolsArray:_array_index:1:error = ""
mail:protocolsArray:_array_index:2:status = "ON"
mail:protocolsArray:_array_index:2:kind = "INCOMING"
mail:protocolsArray:_array_index:2:protocol = "SMTP"
mail:protocolsArray:_array_index:2:state = "RUNNING"
mail:protocolsArray:_array_index:2:service = "MailTransferAgent"
mail:protocolsArray:_array_index:2:error = ""
mail:protocolsArray:_array_index:3:status = "ON"
mail:protocolsArray:_array_index:3:kind = "OUTGOING"
mail:protocolsArray:_array_index:3:protocol = "SMTP"
mail:protocolsArray:_array_index:3:state = "RUNNING"
mail:protocolsArray:_array_index:3:service = "MailTransferAgent"
mail:protocolsArray:_array_index:3:error = ""
mail:protocolsArray:_array_index:4:status = "OFF"
mail:protocolsArray:_array_index:4:kind = "INCOMING"
mail:protocolsArray:_array_index:4:protocol = ""
mail:protocolsArray:_array_index:4:state = "STOPPED"
mail:protocolsArray:_array_index:4:service = "ListServer"
mail:protocolsArray:_array_index:4:error = ""
mail:protocolsArray:_array_index:5:status = "ON"
mail:protocolsArray:_array_index:5:kind = "INCOMING"
mail:protocolsArray:_array_index:5:protocol = ""
mail:protocolsArray:_array_index:5:state = "RUNNING"
mail:protocolsArray:_array_index:5:service = "JunkMailFilter"
mail:protocolsArray:_array_index:5:error = ""
mail:protocolsArray:_array_index:6:status = "ON"
mail:protocolsArray:_array_index:6:kind = "INCOMING"
mail:protocolsArray:_array_index:6:protocol = ""
mail:protocolsArray:_array_index:6:state = "RUNNING"
mail:protocolsArray:_array_index:6:service = "VirusScanner"
mail:protocolsArray:_array_index:6:error = ""
mail:protocolsArray:_array_index:7:status = "ON"
mail:protocolsArray:_array_index:7:kind = "INCOMING"
mail:protocolsArray:_array_index:7:protocol = ""
mail:protocolsArray:_array_index:7:state = "RUNNING"
mail:protocolsArray:_array_index:7:service = "VirusDatabaseUpdater"
mail:protocolsArray:_array_index:7:error = ""
mail:logPaths:Server Error Log = "/Library/Logs/Mail/mail-err.log"
mail:logPaths:IMAP Log = "/Library/Logs/Mail/mail-info.log"
mail:logPaths:Server Log = "/Library/Logs/Mail/mail-info.log"
mail:logPaths:POP Log = "/Library/Logs/Mail/mail-info.log"
mail:logPaths:SMTP Log = "/var/log/mail.log"
mail:logPaths:List Server Log = "/Library/Logs/Mail/listserver.log"
mail:logPaths:Migration Log = "/Library/Logs/MailMigration.log"
mail:logPaths:Virus Log = "/Library/Logs/Mail/clamav.log"
mail:logPaths:Amavisd Log = "/Library/Logs/Mail/amavis.log"
mail:logPaths:Virus DB Log = "/Library/Logs/Mail/freshclam.log"
mail:imapStartedTime = "2013-10-08 08:11:34 +0000"
mail:postfixStartedTime = "2013-10-08 08:12:20 +0000"
mail:servicePortsRestrictionInfo = _empty_array
mail:servicePortsAreRestricted = "NO"
mail:connectionCount = 0
mail:readWriteSettingsVersion = 1
mail:serviceStatus = "ENABLED"

Hooray, Mail is now working and running! You can quickly test to see if SMTP is up by telnetting in. To do that, you would enter this (assuming you’re running the command on that box):

telnet 127.0.0.1 25

If you receive a message like the one below, you’re off to a good start:

Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 mavericks.pretendco.com ESMTP Postfix

Otherwise, if SMTP isn’t running, telnet’s connection will time out. Given that you’re telnetting in on the same machine, it should be almost instant. If the service is running but telnet takes longer than a second, something is horribly wrong.

Extra Configuration Options

With the Mail service, you have a lot of configuration options which you can change, some of which can be changed through the Server app interface, but the majority of changes are done through Terminal. I’m going to go over a few common ones now.

First up, I disable cleartext authentication, and roll with using Open Directory authentication only. To do this, I go to the Mail tab in the Server app, then click on “Edit…” to the right of “Authentication:”.

Mail Authentication Settings.
Mail Authentication Settings.

Click on the drop-down box next to “Authenticate using:” and select “Open Directory”. This will use Kerberos, CRAM-MD5 and Digest-MD5 to authenticate users. I despise cleartext, and since we’ll be disabling POP (or POOP… get it?) very shortly, we’ll want APOP disabled too. Click OK to save the changes.

Next up is disabling POP, and it’s finally time to start using the command line to full effect! The majority of Mail settings can be changed using the friendly serveradmin command. let’s do it now:

sudo serveradmin settings mail:imap:enable_pop = no

Great, we’ve told the Mail service (Dovecot to be precise) that we no longer want to use POP anymore, but that change won’t come into effect until you restart the service. To do that, you’ll need to enter two commands:

sudo serveradmin stop mail
sudo serveradmin start mail

This should only take a few seconds. If you run sudo serveradmin fullstatus mail you’ll see that the STATUS for POP is now OFF. Even though it says RUNNING below, the POP server is actually not running, and does not respond to requests (because it’s not running). The last setting that I would change in a standard setup is the change to an email subject when OS X Server believes the email is spam. I prefer to change it from ***JUNK MAIL*** to ***FLAGGED AS SPAM***. To make this change, you’ll need to command line it:

sudo serveradmin settings mail:postfix:spam_subject_tag = "***FLAGGED AS SPAM***"

Press Enter then do the serveradmin stop start commands (and dance). Now your spam emails will be more obviously marked as spam!

Virtual Domains

One handy feature of the Mail service is the ability to add virtual domains. “What are virtual domains?” I hear you ask. Say, for example, that you have a few domains you want to provide email for. You might have pretendco.com and example.net and you want to provide mail for both of those domains. It’s pretty easy to set up, but there are a few very important caveats that you must be aware of before using this feature.

Mail: No Virtual Domains Configured.
Mail: No Virtual Domains Configured.
  1. Certificates: The Mail service will use the certificate that is assigned to the Mail service, or the default certificate. This means that if you’ve set up Mail to use the certificate mavericks.pretendco.com, any user that adds an account for mavericks.example.net in Mail (or any other email client), they’ll get a certificate warning. While I’m no security expert (nor do I claim to be), I know it’s not good practice to tell your users to blindly skip any certificate mismatch errors. Some people with more restrictive IMAP servers may reject emails because the domain of the email sender doesn’t match the SSL certificate of the server it came from.
  2. Unified Inbox (not in the cool, power-user way): OS X Server only defines one inbox for each user, so emails to jhalpert@pretendco.com and jhalpert@example.net will go to the same inbox, which can get pretty darn confusing. If one account is a personal account and the other is business, users may send to and/or from the wrong account.

With that in mind, setting up virtual domains is a pinch. First off, you’ll need to configure the DNS service in Server to add records for your primary zone. See the image below for how I configured DNS for example.net.

DNS for example.net
DNS for example.net

Next, in Server app, go to the Mail section and click on the Edit button for “Provide Mail for”. Your primary domain will be at the top in the “Domain Name” field, but you’ll see in Virtual Domains, you can provide a list of other domain names that this Mail server can also provide Mail services for. For this example, I’m going to provide Mail for example.net. I’ll click the little + (plus) in the corner to add a new line. Enter in example.net then click OK.

Mail: Virtual Domains List with example.net
Mail: Virtual Domains List with example.net

Quickly verify the MX record has been set up correctly by using the domain information groper (DiG) by entering the following command:

dig mx example.net

You should see that the A record for mavericks.example.net is the same IP address as mavericks.pretendco.com.

DiG of example.net's MX record.
DiG of example.net's MX record.

Next, we need to add the email address for the user(s) who will receive email at both @pretendco.com and @example.net. Make sure you’ve got a copy of Workgroup Manager (don’t? Download here). Open it up and connect to the server. Click on the user you want, then select the “Info” tab. Hit the plus to the right of the “Email” field then enter the new email address.

Multiple Email Accounts for Jim Halpert.
Multiple Email Accounts for Jim Halpert.

The step above is a critical step. If you don’t add the email address for the user into the “virtual alias table”, you’ll get an error like below:

User Doesn't Exist In the Virtual Alias Table.
User Doesn't Exist In the Virtual Alias Table.

Alrighty then, everything is now ready to send and receive email through your virtual domain! Let’s now open our Mail program and set up the account. I’ve already set up the PretendCo email, along with the Example email account also.

Jim Halpert @ PretendCo Inbox.
Jim Halpert @ PretendCo Inbox.
Jim Halpert @ Example Inbox.
Jim Halpert @ Example Inbox.

Different Email Domains, Same Inbox.

As you can see, both emails accounts technically link up to the same inbox, which can be quite confusing. This will get you up and running with virtual domains, but make sure you or your users pay close attention to every email and the address they came to if they’ve got multiple accounts set up in the one mail client.

OS X Mavericks Server - Setting Up An Open Directory Master

In most cases I set up Open Directory after doing the initial server and DNS configuration. Most of the services that OS X Server have hooks into the Open Directory database, so why not configure it now then do the other services after.

If you intend on using Open Directory properly (this includes LDAP and Kerberos), then you must ensure your DNS is healthy before continuing. One wrong character can screw up your entire Kerberos realm and trust me, you don’t want to have to go back and fix that mess (I’d rather punch myself in the face). To check the status of DNS on your server, enter the following command:

sudo changeip -checkhostname

If your result is not “The names match. There is nothing to change.” then you’ve got unhealthy (and sad) DNS. Fix it.

So your DNS is all healthy? Great, let’s continue. Open the Server app, but wait! Open Directory isn’t listed there?! Did Apple kill Open Directory?! Nope, see that Advanced section in the list on your sidebar? Mouse over it and click “Show”. You may now breathe normally. Click on the Open Directory button to view its section in the Server app. Even at the best of times the Open Directory tab in Server is pretty boring, but it’s especially boring when the service isn’t even running. Let’s change that and set up an Open Directory Master! Start by clicking that nice On button in the top-right corner.

The 3 options for directory services in OS X Server.
The 3 options for directory services in OS X Server.

This initial setup wizard hasn’t changed visually since Mountain Lion, but it’s worth going through if you never used Mountain Lion Server! Given that we don’t have an existing Open Directory domain, but we want to make one, we’ll go with the first option, “Create a new Open Directory domain”. Click Next.

Create a Directory Administrator.
Create a Directory Administrator.

By default, OS X Server suggests Directory Administrator and diradmin as the name for the Directory Administrator. My preference is to use Master Directory Administrator, and masterdiradmin respectively, as the server is the Open Directory Master. It’s purely a stylistic choice, just make sure you have the details saved somewhere (in 1Password) I should hope). For this example, I’m using a very basic password, in the real world I would use a randomly generated one, maybe something like this: e9uZxrcsY6vwJe3y6CvK. Click Next

Your Organisation Information in Open Directory.
Your Organisation Information in Open Directory.

Next up is your organisation name and contact email address. Given that these details are used in your SSL certificate or CSR, you must ensure these details are correct. Click Next.

Confirm Your Open Directory Settings.
Confirm Your Open Directory Settings.

In your setup, confirm that your details are all correct before clicking the Set Up button. You don’t want to find down the track you entered wrong details. For the curious, before you click Set Up, open a Terminal window and enter:

tail -f /Library/Logs/slapconfig.log

When you click Set Up you’ll see lots of info fly by as it creates the Open Directory domain.

Your Open Directory domain is now alive!
Your Open Directory domain is now alive!

We can now do a quick test using the dscl command to ensure Open Directory is happy. Using those Directory Administrator credentials we used in set up, we can use a feature of dscl called authonly which, as you would expect, tests for authentication only. See the example below:

dscl /LDAPv3/127.0.0.1 -authonly masterdiradmin masterpass

This says that datasource we’re using is LDAPv3, and the location of that is 127.0.0.1. We then add the flag -authonly to tell dscl we’re just testing for authentication. Finally, we send over the username and password. Given that the password is being sent as cleartext, only do this when you’re physically at the server during set up (or if you do it remotely, do it with a standard user account, not an administrator account). When you press Return (or Enter) to send the command, you won’t get anything returned if it’s correct. Here’s two examples of authentication failure:

Authentication for node /LDAPv3/127.0.0.1 failed. (-14477, eDSInvalidSession)
<dscl_cmd> DS Error: -14477 (eDSInvalidSession)

That was using the wrong password, below is using a user that doesn’t exist in the domain:

Authentication for node /LDAPv3/127.0.0.1 failed. (-14090, eDSAuthFailed)
<dscl_cmd> DS Error: -14090 (eDSAuthFailed)

See the difference? You get an eDSInvalidSession with a bad password, and an eDSAuthFailed with a bad username. It’s handy to know the difference between the two errors, it may come in use one day when testing.

If you’re really anal about testing, you can also test authentication with Kerberos. Here’s the structure of the command:

kinit -V -S host/server_hostname@KERBEROS_REALM od_user@KERBEROS_REALM

First off, we’re using the kinit command, which acquires the initial Kerberos ticket (but can also renew old tickets). Our first flag is -V, which gives us some verbosity when testing kinit - I like verbosity when testing, so we’ll use it in our command. Next up is -S host/server_hostname@KERBEROS_REALM, let’s break this down a little more. the -S means we’re using a realm other than krbtgt/LOCAL.REALM, which is appropriate when testing Kerberos on any other system other than the Kerberos server itself. Technically, we can simplify the kinit command, so that will be reflected below. Finally, is od_user@KERBEROS_REALM. At this stage, the only user in the Open Directory domain is masterdiradmin (or the username for your Directory Administrator). Given that we’re currently only using the one Open Directory server, we know that our KERBEROS_REALM will be MAVERICKS.PRETENDCO.COM (all caps your host name).

kinit -V od_user@KERBEROS_REALM

Now that we’ve got our simplified command structure, use the command below in Terminal:

kinit -V masterdiradmin@MAVERICKS.PRETENDCO.COM

You’ll be prompted to enter in the password for the user (you could use -stdinpass and echo the password, but that’s for another day). If all is successful, you should see something similar to below:

Placing tickets for 'masterdiradmin@MAVERICKS.PRETENDCO.COM' in cache 'API:C7F85975-55A4-4930-8DC3-6D668D209087'

The long UUID you see in the cache string is the unique identifier for that ticket session. To check out the details for your ticket, enter:

klist -v

klist will give you minor details about your ticket, but adding the -v flag will increase the verbosity of output, which you could use for troubleshooting (or just general information gathering or learning). Here’s an example of the output you might expect from klist with the verbosity flag:

Credentials cache: API:C7F85975-55A4-4930-8DC3-6D668D209087
        Principal: masterdiradmin@MAVERICKS.PRETENDCO.COM
    Cache version: 0

Server: krbtgt/MAVERICKS.PRETENDCO.COM@MAVERICKS.PRETENDCO.COM
Client: masterdiradmin@MAVERICKS.PRETENDCO.COM
Ticket etype: aes256-cts-hmac-sha1-96, kvno 1
Ticket length: 368
Auth time:  Oct  6 22:50:57 2013
End time:   Oct  7 08:50:54 2013
Ticket flags: enc-pa-rep, pre-authent, initial, forwardable
Addresses: addressless

Alrighty then, everything appears to be in order! We now have a happy Open Directory Master than is running and accepting connections. Going back to the Server app, you can see a gear in the bottom-left corner of the window where you can change the Global Password Policy, archive the Open Directory Master, or promote a Replica to Master.

We can also get more information regarding Open Directory using serveradmin and the command line. See below for the status:

sudo serveradmin status dirserv

And your output should be as follows:

dirserv:state = "RUNNING"

If you want to get more information regarding the status of Directory Services, do this command:

sudo serveradmin fullstatus dirserv

And your output will be something like this:

dirserv:logPaths:opendirectorydLog = "/var/log/opendirectoryd.log"
dirserv:logPaths:ldapLog = "/var/log/slapd.log"
dirserv:logPaths:passwordServiceServerLog = "/Library/Logs/PasswordService/ApplePasswordServer.Server.log"
dirserv:logPaths:passwordServiceErrorLog = "/Library/Logs/PasswordService/ApplePasswordServer.Error.log"
dirserv:logPaths:kdcLog = "/var/log/krb5kdc/kdc.log"
dirserv:logPaths:slapconfigLog = "/Library/Logs/slapconfig.log"
dirserv:LDAPServerType = "master"
dirserv:state = "RUNNING"
dirserv:ldapSearchBase = "dc=mavericks,dc=pretendco,dc=com"
dirserv:kdcHostedRealm = "MAVERICKS.PRETENDCO.COM"
dirserv:readWriteSettingsVersion = 1

Most of those settings should be pretty obvious, as most of them are log file locations. If you want to get lots more configuration, enter this command:

sudo serveradmin settings dirserv

You’ll get a lot more information regarding the nitty gritty of the Directory Service:

dirserv:selfWrite = yes
dirserv:locales = _empty_array
dirserv:caServer = yes
dirserv:MacOSXODPolicy:Directory Binding = yes
dirserv:MacOSXODPolicy:Configured Security Level:Binding Required = no
dirserv:MacOSXODPolicy:Configured Security Level:Advisory Client Caching = no
dirserv:MacOSXODPolicy:Configured Security Level:Man In The Middle = no
dirserv:MacOSXODPolicy:Configured Security Level:Packet Signing = no
dirserv:MacOSXODPolicy:Configured Security Level:No ClearText Authentications = no
dirserv:MacOSXODPolicy:Configured Security Level:Packet Encryption = no
dirserv:kerberizedRealmList:availableRealms:_array_index:0:dirNodePath = "/LDAPv3/127.0.0.1"
dirserv:kerberizedRealmList:availableRealms:_array_index:0:realmName = "MAVERICKS.PRETENDCO.COM"
dirserv:kerberizedRealmList:defaultRealm = "MAVERICKS.PRETENDCO.COM"
dirserv:PWPolicyInfo:requiresMixedCase = no
dirserv:PWPolicyInfo:passwordMinLen = 0
dirserv:PWPolicyInfo:mustChangeAtFirstLogin = no
dirserv:PWPolicyInfo:passwordMustHaveAlpha = no
dirserv:PWPolicyInfo:requiresSymbol = no
dirserv:PWPolicyInfo:passwordNotAccount = no
dirserv:PWPolicyInfo:passwordDisableFailedLogins = 0
dirserv:PWPolicyInfo:passwordHistoryLen = 0
dirserv:PWPolicyInfo:passwordDisableNumDaysInactive = 0
dirserv:PWPolicyInfo:passwordDisableDate = 0.000000
dirserv:PWPolicyInfo:passwordExpireDays = 0
dirserv:PWPolicyInfo:passwordMustHaveNumber = no
dirserv:PWPolicyInfo:passwordDisableNumDays = 0
dirserv:LDAPDefaultPrefix = "dc=mavericks,dc=pretendco,dc=com"
dirserv:defaultKerbRealmName = "MAVERICKS.PRETENDCO.COM"
dirserv:masterConfig:replicas = _empty_array
dirserv:LDAPSettings:useSSL = yes
dirserv:LDAPSettings:LDAPServerBackend = "config"
dirserv:LDAPSettings:LDAPDataBasePath = "/var/db/openldap/openldap-data"
dirserv:LDAPSettings:maxSearchResults = "11000 size.prtotal=unlimited"
dirserv:LDAPSettings:LDAPSSLIdentityName = "mavericks.pretendco.com"
dirserv:LDAPSettings:LDAPTimeoutUnits = "seconds"
dirserv:LDAPSettings:LDAPSearchBase = "dc=mavericks,dc=pretendco,dc=com"
dirserv:LDAPSettings:searchTimeout = 60
dirserv:LDAPSettings:LDAPSSLSerialNumber = "572599038"
dirserv:treeConfiguration:odTree:_array_index:0:PrimaryMaster = "mavericks.pretendco.com"
dirserv:treeConfiguration:odTree:_array_index:0:IPaddresses:_array_index:0 = "10.1.125.120"
dirserv:treeConfiguration:odTree:_array_index:0:GUID = "AA4D0B74-0073-41EF-82A7-B22E51ECA938"
dirserv:treeConfiguration:odTree:_array_index:0:ReplicaName = "Master"
dirserv:treeConfiguration:odTree:_array_index:0:treeSource = "PrimaryMaster"
dirserv:treeConfiguration:odTree:_array_index:0:Replicas = _empty_array
dirserv:LDAPServerType = "master"

There’s a lot to see there, and most of it’s not worth going through if you’re going to be running a small network with 1 server. Nonetheless, it’s still interesting to see all the configuration details for Directory Services.

OS X Mavericks Server - DNS

Running DNS services for your OS X Mavericks Server can be pretty easy as long as you know how. For a small network with a nice network setup, you shouldn’t have many complications with DNS. If you’ve been following my guides, the minimum you should have completed is the initial set up. It’s a good idea to have DNS all sorted before moving onto any of the other services, setting up DNS is typically one of the first things I would do if the server was to be the DNS server for the LAN.

Go to the Server app and click on the DNS tab. It’ll look pretty empty when the service isn’t running. We’re going to change that. Click the On button to start the DNS services.

For the basic settings, we’ll configure forwarding servers, and what networks will be able to access this DNS server. If you click on the “Edit” button next to “Forwarding Servers:”, we can add the addresses for which this DNS will send queries to if it does not have the authority to do so.

DNS - My Forwarding Settings.
DNS - My Forwarding Settings.

Depending on your modem or ISP configuration, this can vary. If your modem handles all DNS, all you need to enter is the IP address of your modem. Otherwise, if you want to pass on DNS queries to your ISP, you’ll need to enter the two IP addresses for your ISPs DNS servers. Lastly, you have the option of using public DNS servers, like Google’s. Google use 8.8.8.8 and 8.8.4.4 as primary and secondary DNS servers. I would usually use the ISP DNS servers, but it really depends on your network configuration. My advice is to ask your ISP on what details you should enter, they may have specific requirements depending on your internet connection method.

Given that we are setting up this server as the main DNS server for this network, keep “Perform lookups for” ticked, and select “all clients”. This should be the default, but change it if it’s not the default."

DNS - Lookups.
DNS - Lookups.

Next, we’re on to records. If you already have records, the DNS service has constructed them for you, if not, continue! Before we start configuring our own DNS records, click the gear in the bottom-left corner of the window and ensure “Show All Records” is ticked. Without that option ticked, it’s a little vague as to what you’re configuring.

If you go to the + (plus) just next to the gear, the only two options you should have is “Add Primary Zone…” and “Add Secondary Zone…”. We’ll click “Add Primary Zone…”. Given that my domain name is pretendco.com, I’ll enter pretendco.com as the name for the Primary Zone. Next, change the “Zone data is valid for” to 6 hours. 6 hours is long enough for cached DNS values. Now, click OK.

DNS - Adding a Primary Zone.
DNS - Adding a Primary Zone.

Wonderful, we now have a Primary DNS zone. It’s time to add an A record (or Machine record). What this essentially means is that you can assign an individual DNS name to an IP address on the network. Click on the + (plus) again, and click “Add Machine Record”. Keep the “Zone” as pretendco.com (or your zone name if it’s different). Given the subdomain (and name) of my server is mavericks, I’m going to make the Host Name mavericks. The “IP Address” should be the IP address of the server In my case, it’s 10.1.125.250. Now, click “Create”.

DNS - Making a Machine (A) Record.
DNS - Making a Machine (A) Record.

Superb, you now have a DNS name for your server and your domain name. Now you can do a DNS lookup to ensure it’s all working OK. Open up Terminal and enter (replace my IP address with your server’s IP address):

dig -x 10.1.125.120

This does a reverse lookup of the IP address and checks to see what DNS name is associated with that IP address. You should hopefully see something similar to below:

; <<>> DiG 9.8.3-P1 <<>> -x 10.1.125.120
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 27201
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1

;; QUESTION SECTION:
;120.125.1.10.in-addr.arpa.	IN	PTR

;; ANSWER SECTION:
120.125.1.10.in-addr.arpa. 10800 IN	PTR	mavericks.pretendco.com.

;; AUTHORITY SECTION:
125.1.10.in-addr.arpa.	10800	IN	NS	mavericks.pretendco.com.

;; ADDITIONAL SECTION:
mavericks.pretendco.com. 10800	IN	A	10.1.125.120

;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Tue Oct  8 21:03:19 2013
;; MSG SIZE  rcvd: 110

Essentially, what this says is:

  • QUESTION: I’m asking what the DNS name of 10.1.125.120 is
  • ANSWER: The DNS name is mavericks.pretendco.com
  • AUTHORITY: This is the DNS name of the Nameserver that answered our query
  • ADDITIONAL: This shows you the IP address of the Nameserver that answered the query
  • SERVER: The IP address of the DNS server that responded to the query.

If you ran this command on the server, the server that responded should be 127.0.0.1 (aka localhost). If not, you probably don’t have the first DNS server for your network interface as 127.0.0.1. Add that in before you continue.

Finally, we need to add an MX (Mail Exchanger) record for the server, so you can send email. To do this, click on the + (plus) at the bottom-left, then click “Add Mail Exchanger Record…”.

DNS - Add an MX Record.
DNS - Add an MX Record.

For your zone, leave it as the default. For “Mail Server”, it should be the host name for the server you’re using. For example, the host name for my server is mavericks.pretendco.com, so that’s what I’ll enter. For “Priority”, if you have one mail server, enter 0. The priority (or weighting) is used for if you have multiple Mail servers and want to spread the load. The closer number to 0 the server is, the higher priority it is. I’ve only scraped the surface of Mail server priority, it’s worth reading up properly for more technical information. Now you’re ready to click “Create”. So click it.

DNS - It's All Configured!
DNS - It's All Configured!

Your DNS configuration is now done! Naturally, more complex environments will require for DNS configuration, but if you’ve just got one server, this should be all you need to do.

OS X Mavericks Server - Setting Up The Basics

So you’ve got OS X Mavericks and you want a server. Mavericks Server has brought a number of changes to the Server side of OS X. Notable changes include Xcode Server, a continuous integration service for teams of developers for Xcode, SMB2 for file sharing, Caching Server for caching of iOS content and speedy delivery, and Profile Manager with changes including distribution of apps and books purchased from the App Store volume licensing program.

To start, you should have OS X Mavericks installed, and Server downloaded. Initially, we will set a static IP address for the server, never use DHCP for a server. I’m going to use 10.1.125.120, you should use what ever free address is available on your network. Normally, I use .254 for my server (but in this example I already have my server running on .254).

To do this, go to System Preferences (on the server), then click on Network. Select the Ethernet interface (you are using Ethernet right? It’s a must), then next to Configure IPv4, select Manually from the drop-down menu. For my network, I’m entering these details:

  • IP Address: 10.1.125.120
  • Subnet Mask: 255.255.255.0
  • Router: 10.1.125.1
  • DNS Server: 127.0.0.1, 10.1.125.254, 10.1.125.1
My Manually Assigned IPv4 Address for the Server.
My Manually Assigned IPv4 Address for the Server.

Under the DNS Server section, if you intend on using this server as a DNS server, you must have 127.0.0.1 set as the first DNS server. This is so DNS lookups are sent to this server first, then if it is not authoritative, it’ll pass the query on to the next DNS server.

Next, we’re going to set up the hostname for the server. I’m going for mavericks.pretendco.com. Open up your Terminal window, because we’re going to set it up via the command line (you could set it up later via the Server app GUI, but I prefer to do it via the command line before installing Server). First off, we set the HostName of the server.

sudo scutil --set HostName mavericks.pretendco.com

The HostName is the name that is associated with the commands hostname and gethostname. Next, we set LocalHostName.

sudo scutil --set LocalHostName mavericks

This is the local Bonjour host name. Finally, we set the ComputerName, which is the user-friendly name for the system. Some people choose to do the FQDN (or Fully Qualified Domain Name), but my preference is to just give it a nice looking name, given it’s meant to be a user-friendly name. I’m going to go with Mavericks.

sudo scutil --set ComputerName Mavericks

Now, I’m going to install the Server.app. Once you’ve downloaded Server from the Mac App Store, open it to start the initial install. Click through the prompts and enter your administrator username and password, then the Server.app will go through setup of certificates and basic configuration of services.

The stages of Server installation.

Once the setup has been completed, you’ll be shown the Machine section of the Server tree in Server.app

Server App - Main Window.
Server App - Main Window.

Now, let’s ensure DNS is healthy before we start configuring services. DNS is one of the most important parts of OS X Server, and must be healthy if you intend on maintaining a server. Open up a Terminal window and enter the command below:

sudo changeip -checkhostname

Hopefully, you should see something like below:

Primary address     = 10.1.125.120

Current HostName    = mavericks.pretendco.com
DNS HostName        = mavericks.pretendco.com

The names match. There is nothing to change.
dirserv:success = "success"
The 4 most important lines in a servers life.
The 4 most important lines in a servers life.

Hooray! OK, now, it’s a good time to enable SSH, ARD and Screen Sharing. You have two ways around this. First, you can go through the Server GUI by clicking on the server name under the Server tree, then click on Settings. Now, tick the “Allow remote login using SSH for administrators” box, and the “Enable screen sharing and remote management”. Otherwise, go back to your Terminal window and enter the following commands:

sudo serveradmin settings info:enableARD = yes

That’ll enable Apple Remote Desktop remote management so you can manage and get reports from the server. Next, we’ll enable screen sharing.

sudo serveradmin settings info:enableScreenSharing = yes

If you’re on another Mac on the same network, you should see the server pop up in the Shared list in Finder. Finally, we enable SSH by entering this command:

sudo serveradmin settings info:enableSSH = yes

If you went through the Terminal route of remote access, go back to the Server app and you’ll see those boxes have been ticked for you.

Remote Access options enabled in the Server app.
Remote Access options enabled in the Server app.

Lastly, if you’d like to administer the server through the Server app on another computer, you can do the following:

sudo serveradmin settings info:enableRemoteAdministration = yes

And this concludes the initial set up of OS X Mavericks Server. Stay tuned for more posts on configuring the latest version of Apple’s service application!

FreeRADIUS Monitoring with Nagios

After doing a few setups using my buddy Jedda’s excellent article on configuring basic RADIUS on OS X 10.8 Server, I decided I wanted a way to monitor the customers FreeRADIUS server to ensure it’s up and running, and processing requests. Given that we use GroundWork for monitoring, I decided to write a bash script that verifies the process is running, and that it’s processing authentication requests. Queue code!

Take a look at the code on GitHub

ps aux -o tty | grep "/usr/sbin/radius"

Initially, we do a quick process check to make sure FreeRADIUS is even running. If it’s not running, we exit with a critical warning (there’s no point proceeding any further in the script). This script was written for OS X 10.8 Mountain Lion Server, so adjust the path to radius to your liking.

OK, so we’ve verified the radiusd process is running, now it’s time to try and authenticate using valid login credentials to the FreeRADIUS server (in my case, users in the com.apple.access_radius group).

echo "User-Name=<username>,User-Password=<password>,Framed-Protocol=PPP " | radclient -x -r 1 -t 2 localhost:1812 auth testing123 2> /tmp/radius_error

Running the command radclient by itself doesn’t work, you have to pipe radius authentication attribute/value pairs. We use PPP for the Framed-Protocol as we are providing a username and password and using the Point-To-Point Protocol. It’s worth taking a look at RFC 2865 for more information regarding RADIUS and user authentication.

After authentication and server credentials are supplied, we pass all standard error (stderr) to a temporary log file (stored at /tmp/radius_error) so it doesn’t cause the script to produce any unexpected output. I also assign a variable with the contents of that temporary file so we can see what the error was. An incorrect shared secret will throw a stderr, but wrong credentials will just output to stdout.

The next part of our script is an if statement that checks what our authentication attempt has returned, then process accordingly. We check if it’s successful, wrong shared secret, or wrong authentication details. If there’s any other response, I throw a generic error.

./check_radius.sh -u username -p password -h localhost -p 1812 -s sharedsecret

To run the script, you enter the code above (this assumes you’re running with escalated privileges i.e. sudo -s, otherwise prepend the command with sudo). This should be run on the same server as your FreeRADIUS server, or on an authorised Network Access Server (NAS) client.

GroundWork Configuration

First off, we need a command that does the actual check. See below for the command details:

  • Name: check_by_ssh_radius
  • Type: check
  • Command line: $USER1$/check_by_ssh -H $HOSTADDRESS$ -t 60 -l "$USER17$" -C "sudo $USER22$/check_radius.sh -u $ARG1$ -p $ARG2$ -h $ARG3$ -a $ARG4$ -s $ARG5$"
  • Usage: check_by_ssh_radius!ARG1!ARG2!ARG3!ARG4!ARG5

Next, we need a service that uses the command for the check.

  • Name: ssh_radius
  • Service template: use whichever you prefer
  • Check command: check_by_ssh_radius
  • Command line: check_by_ssh_radius!username!password!radius.server!radius.auth.port!shared.secret

Now add this to a host, and follow this command line as an example (as an entry for the host):

check_by_ssh_radius!testuser!test1234!localhost!1812!testing123

Important Note For Testing

Something which I made the mistake of when testing on the FreeRADIUS server was trying to add a client for 127.0.0.1. When I added this client, the damn server wouldn’t start because there was a duplicate entry! FreeRADIUS automatically sets up the local server as a client (NAS), with the default shared secret of testing123. Once you’ve finished testing, you can edit the clients.conf to disable localhost access to the FreeRADIUS server. Please note that if you intend on running this script on the same server as the FreeRADIUS server, don’t disable localhost access!

Performance Data - A Maybe

I had planned to do performance data but I felt the status function through radclient was too unreliable to query. I’ll possibly look into performance data again in a few months.

RRDtool Graph for Nagios' check_disk Command

Over the past few months I’ve been working more and more with RRDtool, and since we use GroundWork at work, I thought I’d start to better the default graphs that come with GroundWork. One of the graphs I thought needed the most improvement is the graph for check_disk. See the old graph below:

The original GroundWork/RRDtool graph for disk usage.
The original GroundWork/RRDtool graph for disk usage.
The new and improved graph for disk usage!
The new and improved graph for disk usage!

Along with my other graphs, I will be using the Outlined Area Graphs colour set for my graph areas and lines.

Without further ado, here is the code for the graph:

rrdtool graph - \
-E \
-P \
-h 180 \
-l 0 \
--grid-dash 1:2 \
-t "<big><b>Disk Utilisation</b></big>" \
-b 1024 \
-X 0 \
-a PNG \
-v "<b>Disk Usage (MB)</b>" \
DEF:diskCurr=rrd_source:ds_source_0:AVERAGE \
DEF:diskWarn=rrd_source:ds_source_1:AVERAGE \
DEF:diskCrit=rrd_source:ds_source_2:AVERAGE \
DEF:diskMax=rrd_source:ds_source_3:AVERAGE \
CDEF:diskPerc=diskCurr,diskMax,/,100,* \
CDEF:cdefDisk=diskCurr \
CDEF:cdefw=diskWarn \
CDEF:cdefc=diskCrit \
CDEF:cdefm=diskMax \
CDEF:warnPerc=diskWarn,diskMax,/,100,* \
CDEF:critPerc=diskCrit,diskMax,/,100,* \
AREA:diskCurr#54EC48:"<b>Space Used\:</b>\g" \
LINE2:cdefDisk#24CB14: \
GPRINT:diskCurr:LAST:" <b>%.0lf MB</b>\g" \
GPRINT:diskPerc:LAST:" <i><b>(%.0lf%%)</b></i>" \
LINE2:cdefm#4D18E4:"<i>Maximum Capacity\:</i>\g" \
GPRINT:cdefm:LAST:" <i>%.0lf MB</i>\n" \
LINE2:cdefw#C9B215:"Warning Threshold\:\g" \
GPRINT:cdefw:AVERAGE:" <i>%.0lf MB</i>\g" \
GPRINT:warnPerc:LAST:" <i>(%0.lf%%)</i>" \
LINE2:cdefc#CC3118:"<i>Critical Threshold\:</i>\g" \
GPRINT:cdefc:LAST:" <i>%.0lf MB</i>\g" \
GPRINT:critPerc:LAST:" <i>(%.0lf%%)</i>\n" \
-c BACK#FFFFFF \
-c CANVAS#FFFFFF \
-c GRID#C0C0C0 \
-c MGRID#404040 \
-c ARROW#FFFFFF \
-Y
  • rrdtool graph - — this will tell rrdtool that we’re making a graph, and the output will go to stdout (needed for GroundWork, otherwise you can put something like disk_usage.png).
  • -E — aka --slope-mode gives the graph a more organic and natural look.
  • -P — uses Pango markup to render all text with HTML, so you can use tags like <b></code> or <code class="inline"><i>. There are more available, you can check out Pango Reference Manual.
  • -h 180 — this sets the height of the graph to 180 pixels.
  • -l 0 — this sets the lowest number (or limit) of the Y-axis to 0, because obviously, a storage device can’t have a negative capacity.
  • --grid-dash 1:2 — gives us nice and small dotted lines for the graph.
  • -t "<big><b>Disk Utilisation</b></big>" — the title for the graph, with Pango-supported HTML to make the title bigger and bolder (as the tags would suggest).
  • -b 1024 — as we are dealing with storage capacity, we measure the data in base2.
  • -X 0 — disables the unit exponent scaling for this graph.
  • -a PNG — formats the output of the graph as a PNG. Depending on what setting you have, it could impact some other settings (i.e. --no-gridfit).
  • -v "<b>Disk Usage (MB)</b>" — this sets a vertical title on the left-side of the Y-axis.
  • DEF:diskCurr=rrd_source:ds_source_0:AVERAGE — this defines a variable called diskCurr which is sourced from the RRD. GroundWork automatically fetches this for you. From the command line, you would manually specify both the path to the RRD, and the data source (i.e. server17.pretendco.com_ssh_disk_boot.rrd:_dev_disk0s2).
  • DEF:diskWarn=rrd_source:ds_source_1:AVERAGE — same as above, but gets the warning level for the disk (in MB).
  • DEF:diskCrit=rrd_source:ds_source_2:AVERAGE — same as above, but gets the critical level for the disk (in MB).
  • DEF:diskMax=rrd_source:ds_source_3:AVERAGE — same as above, but gets the maximum size of the disk (in MB).
  • CDEF:diskPerc=diskCurr,diskMax,/,100,* — this command (CDEF) copies the values of a variable(s), and math can be performed using rpn (Reverse Polish Notation). The standard way of writing this is diskPerc = (diskCurr / diskMax) * 100 as we are calculating the percentage used of the disk. I won’t delve into the complexities of RPN, but take a look at the Wikipedia article.
  • CDEF:cdefDisk=diskCurr — assign another variable called cdefDisk which copies the diskCurr variable.
  • CDEF:cdefw=diskWarn — as above, copy one variable to another.
  • CDEF:cdefc=diskCrit — as above, copy one variable to another.
  • CDEF:cdefm=diskMax — as above, copy one variable to another.
  • CDEF:warnPerc=diskWarn,diskMax,/,100,* — calculate the warning level as a percentage. In standard mathematics, your equation would be warnPerc = (diskWarn / diskMax) * 100.
  • CDEF:critPerc=diskCrit,diskMax,/,100,* — like above, calculate the critical level as a percentage.
  • AREA:diskCurr#54EC48:"<b>Space Used\:</b>\g" — graph the area of the variable diskCurr with the colour #54EC48, then print Space Used: in bold, with a string modifier (\g) to strip whitespace at the end of the string.
  • LINE2:cdefDisk#24CB14: — print a line on the graph that is 2 pixels thick, with the value of cdefDisk and the colour #24CB14. Note the extra colon (:) at the end, this means we’re not printing any string in the legend (below the graph).
  • GPRINT:diskCurr:LAST:" %.0lf MB\g" — print the last value of diskCurr as an integer with MB at the end.
  • GPRINT:diskPerc:LAST:" (%.0lf%%)" — print the percentage of the disk used. Note the double percentage, the first percentage symbol escapes the second symbol (so RRDtool doesn’t think we’re trying to print a number).
  • LINE2:cdefm#4D18E4:"Maximum Capacity\:\g" — draw a line on the graph that represents the maximum capacity of the storage device.
  • GPRINT:cdefm:LAST:" %.0lf MB\n" — print the maximum capacity of the storage device in megabytes (MB).
  • LINE2:cdefw#C9B215:"Warning Threshold\:\g" — draw a line on the graph that represents the warning threshold of space used.
  • GPRINT:cdefw:AVERAGE:" %.0lf MB\g" — print the warning threshold in MB.
  • GPRINT:warnPerc:LAST:" (%0.lf%%)" — print the warning threshold as a percentage.
  • LINE2:cdefc#CC3118:"Critical Threshold\:\g" — draw a line on the graph that represents the critical threshold of space used.
  • GPRINT:cdefc:LAST:" %.0lf MB\g" — print the critical threshold in MB.
  • GPRINT:critPerc:LAST:" (%.0lf%%)\n" — print the critical threshold has a percentage.
  • -c BACK#FFFFFF — change the background colour of the graph to #FFFFFF (white).
  • -c CANVAS#FFFFFF — change the canvas colour (the actual graph itself) of the graph to #FFFFFF (white).
  • -c GRID#C0C0C0 — change the grid colour to #C0C0C0 (light gray).
  • -c MGRID#404040 — change the major grid colour to #404040 (dark gray).
  • -c ARROW#FFFFFF — change the arrow colours to #FFFFFF (white) to hide them.
  • -Y — enables the nice dynamic Y-axis grid that gives you whole numbers and ensures you don’t have too many horizontal lines that could make the graph messy or hard to understand.

You can also take a look at the gist for the code.

Enjoy!

PSA: Fake ATO Refund Phishing Email

This morning I received an email purportedly from the Australian Tax Office (ATO) which, at first, excited me (I had just woken up) because I was getting another refund! Hooray! But… there’s always a catch. Upon actually reading the email I noticed several red flags which made me realise that the email is in fact, a phishing email.

The phishing email as seen on my Mac.
The phishing email as seen on my Mac.

First off, the refund amount is listed as AUD. Given the Australian Tax Office operates only in Australia, there is no reason that they would have to specify what currency the refund amount is in, are they all of a sudden going to refund in USD, EUR? I don’t think so. Secondly, the website is cloudaccess.net, not ato.gov.au. The Australian Tax Office would never use another domain for any of their pages.

The last paragraph makes mention of an SSL certificate, yet the resource for the aforementioned link is http, not https (which all secure websites should be). Given how the banks have been training people to look out for the lock for some years now I think this would be a obvious red flag for many people.

The goofballs who made the email even used @ato.com.au as the reply email address. Any Australian resident who uses the internet should know that the Australian Tax Office is a government branch, and will use the .gov.au TLD, not .com.au

For more details regarding the Australian Tax Office and online security, I recommend checking out the ATO’s article titled “Online Security” as it contains examples of past scams, and how to identify a fraudulant email.

Stay safe out there!

1 2 3 13 14 15