krypted.com

Tiny Deathstars of Foulness

Bushel shipping a new feature this week call Blueprints. Blueprints are similar to groups, and allow you to assign different options in Bushel to different devices that have a blueprint assigned to them. This also allows you to define one device per blueprint and therefore have different options for different computers. Pretty cool on a few different fronts. And it provides a lot of flexibility for some really, really cool new features we’ve planned for the product.

Introducing_Blueprints

For more on this great new feature, check out this great article from the new Bushel Product Manager, Michael Devins.

December 10th, 2015

Posted In: Bushel, iPhone

Tags: , , ,

There are a number of ways to create groups in OS X Server 5, running on Yosemite or El Capitan. The first is using the Server app, the second is using Workgroup Manager (which requires a little work to get working in El Capitan), the third is using the Users & Groups System Preference pane and the fourth is using the command line. In this article we will look at creating groups in the Server app.

Once a server has been an Open Directory Master all user and group accounts created will be in the Local Network Group when created in Server app. Before that, all user and group objects are stored locally when created in Server app. Once promoted to an Open Directory server, local groups must be created in Workgroup Manager, the Users & Groups System Preference pane or using a command line tool appropriate for group management.

 To create a new group, open the Server app and then click on Groups in the ACCOUNTS list of the Server app sidebar. From here, you can switch between the various directory domains accessible to the server using the drop-down list available. Click on the plus sign to create a local network group.
Screen Shot 2015-09-07 at 11.59.07 PM
At the New Group screen, provide a name for the group in the Full Name field. This can have spaces. Then create a short name for the group in the Group Name field. This should not have spaces.
Screen Shot 2015-09-07 at 11.59.07 PM
Click Done when you have supplied the appropriate information and the group is created. Once done, double-click on the group to see more options.
 Screen Shot 2015-09-08 at 12.00.18 AM
Here, use the plus sign (“+”) to add members to the group or highlight members and use the minus sign (“-“) to remove users from the group. You can also choose to use the following options:
  • Mailing Lists: Lists that are connected to the group.
  • Members: The users that are part of the group
  • Give this group a shared folder: Creates a shared directory for the group, or a group with an ACL that grants all group members access.
  • Make group members Messages buddies: Adds each group member to each other group members buddy list in the Messages client.
  • Enable group mailing list: Enables a list using the short name of the group where all members receive emails to that address.
  • Create Group Wiki: Opens the Wiki interface for creating a wiki for the group.
  • Keywords: Keywords/tags to help locate users.
  • Notes: Notes about users.

Once changes have been made, click Done to commit the changes.

October 3rd, 2015

Posted In: Mac OS X Server

Tags: , , , , ,

There are a number of ways to create groups in OS X Mavericks Server (Server 3). The first is using the Server app, the second is using Workgroup Manager (which could be running on an older operating system and connecting to the Mavericks Server in question), the third is using the Users & Groups System Preference pane and the fourth is using the command line. In this article we will look at creating groups in the Server app.

Once a server has been an Open Directory Master all user and group accounts created will be in the Local Network Group when created in Server app. Before that, all user and group objects are stored locally when created in Server app. Once promoted to an Open Directory server, local groups must be created in Workgroup Manager, the Users & Groups System Preference pane or using a command line tool appropriate for group management.

 To create a new group, open the Server app and then click on Groups in the ACCOUNTS list of the Server app sidebar. From here, you can switch between the various directory domains accessible to the server using the drop-down list available. Click on the plus sign to create a local network group.

Groups1

At the New Group screen, provide a name for the group in the Full Name field. This can have spaces. Then create a short name for the group in the Group Name field. This should not have spaces.

Groups2

Click Done when you have supplied the appropriate information and the group is created. Once done, double-click on the group to see more options.

Groups3

Here, use the plus sign (“+”) to add members to the group or highlight members and use the minus sign (“-”) to remove users from the group. You can also choose to use the following options:

  • Give this group a shared folder: Creates a shared directory for the group, or a group with an ACL that grants all group members access.
  • Make group members Messages buddies: Adds each group member to each other group members buddy list in the Messages client.
  • Enable group mailing list: Enables a list using the short name of the group where all members receive emails to that address.
  • Create Group Wiki: Opens the Wiki interface for creating a wiki for the group.
  • Keywords: Keywords/tags to help locate users.
  • Notes: Notes about users.

Once changes have been made, click Done to commit the changes.

October 16th, 2014

Posted In: Mac OS X Server

Tags: , , , , , , , ,

Configuring web services is as easy in OS X Mountain Lion Server (10.8) as it has ever been. To set up the default web portal, simply open the Server app, click on the Websites service and click on the ON button.

After a time, the service will start. Once running, click on the View Server Website link at the bottom of the pane.

Provided the stock OS X Server page loads, you are ready to use OS X Server as a web server.

Before we setup custom sites, there are a few things you should know. The first is, the server is no longer really designed to remove the default website. So if you remove the site, your server will exhibit inconsistent behavior. Also, don’t remove the files that comprise the default site. Instead just add sites, which is covered next. Webmail is gone. You don’t have to spend a ton of time looking for it as it isn’t there. Also, Mountain Lion Server adds web apps, which we’ll briefly review later in this article as well.  Finally, enabling PHP and Python on sites is done globally, so this setting applies to all sites hosted on the server.

Now that we’ve got that out of the way, let’s add our first custom site. Do so by clicking on the plus sign. At the New Web Site pane, you’ll be prompted for a number of options. The most important is the name of the site, with other options including the following:

  • Domain Name: The name the site is accessible from. The default sites do not have this option as they are accessible from all names that resolve to the server.
  • IP Address: The IP address the site listens on. Any means the site is available from every IP address the server is configured to use. The default websites do not have this option as they are accessible from all addresses automatically
  • Port: By default, sites without SSL run on port 80 on all network interfaces, and sites with SSL run on port 443 on all network interfaces. Use the Port field to use custom ports (e.g., 8080). The default sites do not have this option as they are configured to use 80 and 443 for default and SSL-based communications respectively.
  • SSL Certificate: Loads a list of SSL certificates installed using Keychain or the SSL Certificate option in the Settings pane of the Server application
  • Store Site Files In: The directory that the files that comprise the website are stored in. These can be placed into the correct directory using file shares or copying using the Finder. Click on the drop-down menu and then select Other to browse to the directory files are stored in.
  • Who Can Access: By default Anyone (all users, including unauthenticated guests) can access the contents of sites. Clicking on Anyone and then Customize… brings up the “Restrict access to the following folders to a chosen group” screen, where you can choose web directories and then define groups of users who can access the contents.
  • Additional Domains: Click on the Edit… button to bring up a simple list of domain names the the site also responds for (e.g. in addition to krypted.com, add www.krypted.com).
  • Redirects: Click on the Edit… button to bring up a list of redirects within the site. This allows configuring redirects to other sites. For example, use /en to load english.krypted.com or /cn to load china.krypted.com).
  • Aliases: Click on the Edit… button to load a list of aliases. This allows configuring redirects to folders within the same server. For example, /en loads /Library/Server/Web/Data/Sites/Default
  • Index Files: Click on the Edit… button to bring up a list of pages that are loaded when a page isn’t directly indicated. For example, when visiting krypted.com, load the wp.php page by default.
  • Advanced Options: The remaining options are available by clicking on the “Edit Advanced Settings…” button.
  • Enable Server Side Includes: Allows administrators to configure leveraging includes in web files, so that pieces of code can be used across multiple pages in sites.
  • Allow overrides using .htaccess files: Using a .htaccess file allows administrators to define who is able to access a given directory, defining custom user names and passwords in the hidden .htaccess file. These aren’t usually required in an OS X Server web environment as local and directory-based accounts can be used for such operations. This setting enables using custom .htaccess files instead of relying on Apple’s stock web permissions.
  • Allow folder listing: Enables folder listings on directories of a site that don’t have an Index File (described in the non-Advanced settings earlier).
  • Allow CGI execution: Enables CGI scripts for the domain being configured.
  • Use custom error page: Allows administrators to define custom error pages, such as those annoying 404 error pages that load when a page can’t be found
  • Make these web apps available on this website: A somewhat advanced setting, loads items into the webapps array, which can be viewed using the following command:  sudo serveradmin settings web:definedWebApps

Once you’ve configured all the appropriate options, click on Done to save your changes. The site should then load. Sites are then listed in the list of Websites.

The Apache service is most easily managed from the Server app, but there are too many options in Apache to really be able to put into a holistic graphical interface. The easiest way to manage the Websites service in OS X Mountain Lion server is using the serveradmin command. Apache administrators from other platforms will be tempted to use the apachectl command to restart the Websites service. Instead, use the serveradmin command to do so. To start the service:

sudo serveradmin start web

To stop the service(s):

sudo serveradmin stop web

And to see the status:

sudo serveradmin fullstatus web

Fullstatus returns the following information:

web:health = _empty_dictionary
web:readWriteSettingsVersion = 1
web:apacheVersion = "2.2"
web:servicePortsRestrictionInfo = _empty_array
web:startedTime = "2012-08-13 23:01:42 +0000"
web:apacheState = "RUNNING"
web:statusMessage = ""
web:ApacheMode = 2
web:servicePortsAreRestricted = "NO"
web:state = "RUNNING"
web:setStateVersion = 1

While the health option typically resembles kiosk computers in the Computer Science departments of most major universities, much of the rest of the output can be pretty helpful including the Apache version, whether the service is running, any restrictions on ports and the date/time stamp that the service was started.

To see all of the settings available to the serveradmin command, run it, followed by settings and then web, to indicate the Websites service:

sudo serveradmin settings web

The output is pretty verbose and can be considered in two sections, the first includes global settings across sites as well as the information for the default sites that should not be deleted:

web:defaultSite:documentRoot = "/Library/Server/Web/Data/Sites/Default"
web:defaultSite:serverName = ""
web:defaultSite:realms = _empty_dictionary
web:defaultSite:redirects = _empty_array
web:defaultSite:enableServerSideIncludes = no
web:defaultSite:customLogPath = ""/var/log/apache2/access_log""
web:defaultSite:webApps = _empty_array
web:defaultSite:sslCertificateIdentifier = ""
web:defaultSite:fullSiteRedirectToOtherSite = ""
web:defaultSite:allowFolderListing = no
web:defaultSite:serverAliases = _empty_array
web:defaultSite:errorLogPath = ""/var/log/apache2/error_log""
web:defaultSite:fileName = "/Library/Server/Web/Config/apache2/sites/0000_any_80_.conf"
web:defaultSite:aliases = _empty_array
web:defaultSite:directoryIndexes:_array_index:0 = "index.html"
web:defaultSite:directoryIndexes:_array_index:1 = "index.php"
web:defaultSite:directoryIndexes:_array_index:2 = "/wiki/"
web:defaultSite:directoryIndexes:_array_index:3 = "default.html"
web:defaultSite:allowAllOverrides = no
web:defaultSite:identifier = "37502141"
web:defaultSite:port = 80
web:defaultSite:allowCGIExecution = no
web:defaultSite:serverAddress = "*"
web:defaultSite:requiresSSL = no
web:defaultSite:proxies = _empty_dictionary
web:defaultSite:errorDocuments = _empty_dictionary
web:defaultSecureSite:documentRoot = "/Library/Server/Web/Data/Sites/Default"
web:defaultSecureSite:serverName = ""
web:defaultSecureSite:realms = _empty_dictionary
web:defaultSecureSite:redirects = _empty_array
web:defaultSecureSite:enableServerSideIncludes = no
web:defaultSecureSite:customLogPath = ""/var/log/apache2/access_log""
web:defaultSecureSite:webApps = _empty_array
web:defaultSecureSite:sslCertificateIdentifier = "com.apple.systemdefault.9912650B09DE94ED160146A3996A45EB3E39275B"
web:defaultSecureSite:fullSiteRedirectToOtherSite = ""
web:defaultSecureSite:allowFolderListing = no
web:defaultSecureSite:serverAliases = _empty_array
web:defaultSecureSite:errorLogPath = ""/var/log/apache2/error_log""
web:defaultSecureSite:fileName = "/Library/Server/Web/Config/apache2/sites/0000_any_443_.conf"
web:defaultSecureSite:aliases = _empty_array
web:defaultSecureSite:directoryIndexes:_array_index:0 = "index.html"
web:defaultSecureSite:directoryIndexes:_array_index:1 = "index.php"
web:defaultSecureSite:directoryIndexes:_array_index:2 = "/wiki/"
web:defaultSecureSite:directoryIndexes:_array_index:3 = "default.html"
web:defaultSecureSite:allowAllOverrides = no
web:defaultSecureSite:identifier = "37502140"
web:defaultSecureSite:port = 443
web:defaultSecureSite:allowCGIExecution = no
web:defaultSecureSite:serverAddress = "*"
web:defaultSecureSite:requiresSSL = yes
web:defaultSecureSite:proxies = _empty_dictionary
web:defaultSecureSite:errorDocuments = _empty_dictionary
web:dataLocation = "/Library/Server/Web/Data"
web:mainHost:keepAliveTimeout = 15.000000
web:mainHost:maxClients = "50%"

The second section is per-site settings, with an array entry for each site:

web:customSites:_array_index:0:documentRoot = "/Library/Server/Web/Data/Sites/www2.krypted.com"
web:customSites:_array_index:0:serverName = "www2.krypted.com"
web:customSites:_array_index:0:realms = _empty_dictionary
web:customSites:_array_index:0:redirects = _empty_array
web:customSites:_array_index:0:enableServerSideIncludes = no
web:customSites:_array_index:0:customLogPath = "/var/log/apache2/access_log"
web:customSites:_array_index:0:webApps = _empty_array
web:customSites:_array_index:0:sslCertificateIdentifier = ""
web:customSites:_array_index:0:fullSiteRedirectToOtherSite = ""
web:customSites:_array_index:0:allowFolderListing = no
web:customSites:_array_index:0:serverAliases = _empty_array
web:customSites:_array_index:0:errorLogPath = "/var/log/apache2/error_log"
web:customSites:_array_index:0:fileName = "/Library/Server/Web/Config/apache2/sites/0000_any_80_www2.krypted.com.conf"
web:customSites:_array_index:0:aliases = _empty_array
web:customSites:_array_index:0:directoryIndexes:_array_index:0 = "index.html"
web:customSites:_array_index:0:directoryIndexes:_array_index:1 = "index.php"
web:customSites:_array_index:0:directoryIndexes:_array_index:2 = "/wiki/"
web:customSites:_array_index:0:directoryIndexes:_array_index:3 = "default.html"
web:customSites:_array_index:0:allowAllOverrides = no
web:customSites:_array_index:0:identifier = "41179886"
web:customSites:_array_index:0:port = 80
web:customSites:_array_index:0:allowCGIExecution = no
web:customSites:_array_index:0:serverAddress = "*"
web:customSites:_array_index:0:requiresSSL = no
web:customSites:_array_index:0:proxies = _empty_dictionary
web:customSites:_array_index:0:errorDocuments = _empty_dictionary

The final section (the largest by far) includes array entries for each defined web app. The following shows the entry for a Hello World Python app:

web:definedWebApps:_array_index:15:requiredWebAppNames = _empty_array
web:definedWebApps:_array_index:15:includeFiles:_array_index:0 = "/Library/Server/Web/Config/apache2/httpd_wsgi.conf"
web:definedWebApps:_array_index:15:requiredModuleNames:_array_index:0 = "wsgi_module"
web:definedWebApps:_array_index:15:startCommand = ""
web:definedWebApps:_array_index:15:sslPolicy = 0
web:definedWebApps:_array_index:15:requiresSSL = no
web:definedWebApps:_array_index:15:requiredByWebAppNames = _empty_array
web:definedWebApps:_array_index:15:launchKeys = _empty_array
web:definedWebApps:_array_index:15:proxies = _empty_dictionary
web:definedWebApps:_array_index:15:preflightCommand = ""
web:definedWebApps:_array_index:15:stopCommand = ""
web:definedWebApps:_array_index:15:name = "com.apple.webapp.wsgi"
web:definedWebApps:_array_index:15:displayName = "Python "Hello World" app at /wsgi"

Each site has its own configuration file defined in the array for each section. By default these are stored in the /Library/Server/Web/Config/apache2/sites directory, with /Library/Server/Web/Config/apache2/sites/0000_any_80_www2.krypted.com.conf being the file for the custom site we created previously. As you can see, many of the options available in the Server app are also available in these files:

<VirtualHost *:80>
ServerName www2.krypted.com
ServerAdmin admin@example.com
DocumentRoot "/Library/Server/Web/Data/Sites/www2.krypted.com"
DirectoryIndex index.html index.php /wiki/ default.html
CustomLog /var/log/apache2/access_log combinedvhost
ErrorLog /var/log/apache2/error_log

<IfModule mod_ssl.c>
SSLEngine Off
SSLCipherSuite "ALL:!aNULL:!ADH:!eNULL:!LOW:!EXP:RC4+RSA:+HIGH:+MEDIUM"
SSLProtocol -ALL +SSLv3 +TLSv1
SSLProxyEngine On
SSLProxyProtocol -ALL +SSLv3 +TLSv1
</IfModule>

<Directory "/Library/Server/Web/Data/Sites/www2.krypted.com">
Options All -Indexes -ExecCGI -Includes +MultiViews
AllowOverride None
<IfModule mod_dav.c>
DAV Off
</IfModule>
<IfDefine !WEBSERVICE_ON>
Deny from all
ErrorDocument 403 /customerror/websitesoff403.html
</IfDefine>
</Directory>

</VirtualHost>

The serveradmin command can also be used to run commands. For example, to reset the service to factory defaults, delete the configuration files for each site and then run the following command:

sudo serveradmin command web:command=restoreFactorySettings

The final tip I’m going to give in this article is when to make changes with each app. I strongly recommend making all of your changes in the Server app when possible. When it isn’t, use serveradmin and when you can’t make changes in serveradmin, only then alter the configuration files that come with the operating system by default. I also recommend keeping backups of all configuration files that are altered and a log of what was altered in each, in order to help piece the server back together should it become unconfigured miraculously when a softwareupdate -all is run next.

August 15th, 2012

Posted In: Mac OS X, Mac OS X Server

Tags: , , , , , , , , , , , , , , , , , , , , , , , , , , , ,

Over the years, the terms Magic, Golden, Triangle, Augments, Directory, Domains and Active have given the administrators of Mac OS X environments fits. So when you think about using Active Directory to manage iOS devices through the Profile Manager service, built into Lion Server, you may think that it’s a complicated thing to piece together. You may remember those days when you had to manually craft service principals because xgrid wouldn’t play nice with Acive Directory, or you might think of twisting augmented records to support CalDAV. But you’re gonna’ have to forget all that, ’cause getting Profile Manager to talk to Active Directory is one of the easiest things you’ll do.

Before we get started, architecture. Every Profile Manager instance is an Open Directory Master. Apple has included a local group in Mac OS X Server called Profile Manager ACL. Users and groups from any directory domain that can be viewed in dscl can be added to this group. Adding objects to this group enables them to authenticate to the MyDevices portal but not administrate. Kerberos isn’t really used here, nor are nested groups. You’ll apply policies directly to Active Directory groups in Profile Manager. For many long-term Apple administrators, this paragraph is all you need to read. If not, please continue on.

To get started, first set Profile Manager up, as shown in a previous article I did. Once configured, verify that Open Directory or local clients can authenticate, bind to Active Directory.

Bind to Active Directory

From within System Preferences, click on the Users & Groups System Preference pane and click on Login Options. Then click on the Edit… button for the Network Account Server. From here, click on the plus sign (“+”) and enter the domain name into the Server field.

Once bound, you will see the server listed. At this point, if you try to authenticate to the MyDevices portal as an Active Directory user, you will be able to authenticate, but you will not have permission to enroll devices. To log in, access the web service at the address of the server followed by /MyDevices (e.g. https://mdm.pretendco.com/MyDevices).

Provide the user name and password to the service. The Active Directory users are unable to access the MyDevices service.

Nest Groups Using Workgroup Manager

Click on Logout and we’ll fix this. There is no further configuration required for the Active Directory groups to function properly in regards to how they work with the server. However, we will need to open Workgroup Manager and nest some groups. You might think that you’d be doing something all kinds of complicated, but notsomuch. You also might think that you would be nesting the Active Directory users and groups inside Open Directory groups, given that you have to enable Open Directory in order to use Profile Manager. Again, notsomuch. To nest the groups, browse to the local directory and then then click on the com.apple.access_devicemanagement group.

Click on the lock icon to unlock the directory domain, authenticating when prompted.

Click on the Members tab and then click on the plus sign (“+”) to add members to the group.

Then in the menu that slid out, click on the domain browser at the top of that menu and select the Active Directory entry.

Test Access

Drag the user or group from the menu into the list of members and then click on the Save button.

Now log in again using the MyDevices portal and you’ll be able to Enroll. From within Profile Manager (log in here as a local administrator), you’ll see all of the users and groups and be able to apply policies directly to them by clicking on the Edit button for each (the information isn’t saved in the directory service on the server, but is cached into the directory service client on the client when using Mac OS X 10.7, Lion based clients).

 

Moving Mac OS X Management From MCX

You keep hearing that you need to move some of your managed preferences to profiles (or Profile Manager in most cases), but you can’t really think about that until you get Profile Manager integrated with Active Directory, can you? And getting those pesky iOS devices working with Active Directory style policies has been on your radar, but really, who has time?

Profiles then have a few distinct benefits over Managed Preferences (MCX) for some, which we’ll look at through the lens of Profile Manager. The first is that they’re instant. You can make a change to a profile on a device enrolled in an MDM service and you instantly see the changes on the client (most profile settings that is, not all), rather than having to log the client out and then back in. You can also wipe and lock devices and the interface is easier (I mean, no nesting thankyouverymuch).

But there are a few drawbacks as well. You can’t cluster Profile Manager, so there are some benefits to using 3rd party services in a move to profile based management. You also manage settings using the Always option, rather than being able to use the Once or Often settings. You can use custom property lists, though and importantly, MCX is used to actually implement most of these profiles on client systems, so those skills you’ve been honing for managing Managed Client workflows will not be totally lost in the transition. Overall, I had initially thought that management by profile would be much less granular than management via managed preferences, but I’ve found ways around any issues and have found it’s actually much easier and works as reliably as dual directory or Active Directory based managed preferences worked.

April 3rd, 2012

Posted In: iPhone, Mac OS X, Mac OS X Server, Mac Security, Mass Deployment

Tags: , , , , , , , , , , , ,

The directory services command line (dscl) command can be used to create a group. Here we’re going to use dscl to create a group called Local Admins (or ldadmins for short).  First up, create the group:

dscl . create /Groups/ladmins

Now give our ladmins group the full name by creating the name key:

dscl . create /Groups/ladmins RealName “Local Admins”

Now to give the group a password:

dscl . create /Groups/ladmins passwd “*”

Now let’s give the group a Group ID:

dscl . create /Groups/ladmins gid 400

That wasn’t so hard, but our group doesn’t have any users.

dscl . create /Groups/ladmins GroupMembership localadmin

Why create a group with just one member though… We can’t use the create verb again, with dscl or we’ll overwrite the existing contents of the GroupMembership field, so we’re going to use append instead:

dscl . append /Groups/ladmins GroupMembership 2ndlocaladmin

If you use dscl to read the group:

dscl . read /Groups/ladmins

You’ll notice that because it was created through dscl it has a Generated ID of its own.  You can easily nest other groups into this one using their Generated IDs as well:

dscl . create /Groups/ladmins GroupMembers 94B6B550-5369-4028-87A8-0ABAB01AE396

The “.” that we’ve been using has been interchangeable (in this case) with /Local/Default. Now let’s look at making a little shell script to do a few of the steps to use with imaging, touch a file called createladmins.bash and then give it the following contents:

dscl . create /Groups/ladmins
dscl . create /Groups/ladmins RealName “Local Admins”
dscl . create /Groups/ladmins passwd “*”
dscl . create /Groups/ladmins gid 400
dscl . create /Groups/ladmins GroupMembership localadmin
dscl . append /Groups/ladmins GroupMembership 2ndlocaladmin

If you then want to hide these admins, check out my cheat sheet here:
http://krypted.com/mac-os-x/hiding-admin-users-in-mac-os-x/

September 7th, 2009

Posted In: Mac OS X, Mass Deployment

Tags: , , , , , , , , ,

Have you noticed all those groups in Mac OS X with names that start with an underscore (_)?  What do they mean?  Well, that’s just the naming convention in 10.5 for service accounts.  So in most flavors of *nix you would have an lpadmin group but in OS X you now have _lpadmin instead.  Nothing sinister or weird, just a naming convention.

August 2nd, 2008

Posted In: Mac OS X, Mac OS X Server

Tags: ,

So I’ve created a bunch of groups on FaceBook.  Groups for 318, Mac OS X Server, Mac OS X, etc.  Now what…

January 4th, 2008

Posted In: Social Networking

Tags: ,

Is it me or has LinkedIn been saying they’ll make the Groups feature better for a long, long time…  🙁

January 2nd, 2008

Posted In: Social Networking

Tags: , ,