Automated Coder

Exploring the Code of CruiseControl.Net

Archive for October, 2008

CruiseControl.Net Blogs

Posted by Craig Sutherland on 31 October, 2008

Since I’ve been involved in development for CruiseControl.Net I’ve spent a lot of time writing in this blog about what I’m doing and why. For me I have two reasons for doing this:

  • As a journal on what I’m doing so I can go back and understand why I did something a certain way.
  • So other people can learn about what I’m doing and (hopefully) provide feedback.

At the same time I enjoy looking to see what else is happening, both in CruiseControl.Net and in development in general. As such I read a number of different blogs, mainly with the aim of learning how to be a better developer.

So, other than me, who else is writing about CruiseControl.Net. Here are the blogs that I know of that sometimes include blogs on CruiseControl.Net:

Some post more actively on CruiseControl.Net than others, and most are about using CruiseControl.Net (I haven’t been able to find anyone else who is writing about developing for CruiseControl.Net).

And of course, another great area for information on what’s happening with the project is the mailing lists.

PS. I’m still on holiday, I’m just on my way back. When I get back on Sunday I’ll start writing up some of what I’ve been doing in CruiseControl.Net while I’ve been on holiday.

Posted in CruiseControl.Net | Tagged: | Leave a Comment »

Holiday Time

Posted by Craig Sutherland on 10 October, 2008

Holiday, Here I Come!

Tomorrow I’m flying out of the country for a three week holiday in China. During the holiday I won’t have Internet access (although I will take my laptop with me), so i won’t be posting anything or submitting any code changes.

However, I’ve pretty much finished my security changes for the moment. I’ve had a chat with David and when I return from my holidays I’ll start to merge the changes into the trunk. This means security will start to appear in the regular nightly builds from the beginning of November. If you have any ideas, concerns or issues, let me know and I can address them before I start the merge :)

Future Work

Since I’ll be away on holiday, I thought I’d do a bit of brain-storming on what I’d like to see in CruiseControl.Net in the future. These are just ideas at the moment – and some very vague at that. But I’m putting them down here so if people want to send feedback they can (and so I don’t forget them).

Server

  • Finish the XMPP publisher (need to do some work on securing passwords and I’ll like to submit a patch to fix an issue I have with the XMPP library).
  • Add a zip publisher to compres the log files (maybe to include as part of e-mails).
  • Add the ability to extract and pass error information after a successful build. It would be nice to have CCTray tell us why a build failed.
  • Add the ability to run WWF workflows in the server.

CCTray

  • Allow grouping of projects for a server.
  • Allow sending a force/abort build request to multiple projects.
  • Add additional information to the queue display (e.g. time added, priority), plus ability to clear an entire queue.
  • Retrieve task status information from the server and display it (similar to how the dashbaord does).

Web Dashboard

  • Add some AJAX functionality to the dashboard to improve its resposiveness and decrease download size.

Security

  • Add some more authentication methods (e.g. to handle integrated security in the dashboard, etc.)
  • Add an implementation of ISecurityManager that allows security settings to be changed without restarting CruiseServer.
  • Additional auditing functionality (alternate loggers/formats, more fine-grained control, etc).
  • Expose the audit log via the dashboard.
  • Send allowed rights to the clients, so non-allowed rights can be disabled.
  • Send allowed authentication options to the client (including a default).
  • Allow dashboard to have multiple security authentication models (perhaps even allow it to be set from the server).
  • Add secure channels to both .Net remoting and the web dashboard (to prevent man-in-the-middle attacks).

Tools

  • Would be nice to have a configuration validation tool. This would load in the config file and validate each section. However in contrast to the console application it would validate the entire file – not just stop at the first error it finds. There are also open some doors to build “smart” validation into the tool.
  • Add security into the command-line tool (CCCmd).
  • Complete the WIX installer – including getting IIS installation to work.
  • Updating the XSD file to include all the security elements and attributes.

Anyway, these are just ideas. I’ll take a look into some of them when I get back :)

Posted in CruiseControl.Net, Security | Tagged: , | Leave a Comment »

Security Enhancements 3: Auditing

Posted by Craig Sutherland on 10 October, 2008

You Did What?!

Authentication and authorisation can be looked at as proactive parts of security – they stop people from doing things that they shouldn’t. Auditing is more of a reactive process – it looks at what has happened and allows an administrator to change settings based on past actions. While being proactive is great, auditing is often critical to understanding why something happened that shouldn’t have. Therefore being able to audit events is often a critical part of security.

This post will look at modifying the security framework to enable adding audit loggers to the system.

The Plan

Since people will have different auditing requirements, I’m going to allow people to add audit loggers as part of the configuration. Audit loggers will be optional and they can also be specified as many times as required.

Ideally audit loggers should be available at both the project and the server level, but for my initial changes I’m only going to add them at the server level. Later on I may look at also adding them to the project level, but as this will add to the overall complexity I’m just going to handle the server level.

Managing the Event

The starting point for this change is ISecurityManager. I have added a new method called LogEvent() – this will send a log request onto a set of event loggers (more on these to come). The signature for this event is as follows:

void LogEvent(string projectName,
     string userName,
     SecurityEvent eventType,
     SecurityRight eventRight,
     string message);

Yes, there is a large amount of arguments. The reason behind this is to provide the audit loggers with as much information as possible, so they can decide what to do with the information. To allow auditing of additional event types (e.g. Login/Logout), I added a new enumeration – SecurityEvent. Initially I used SecurityPermission, but that only had a limited range of possibilities. SecurityEvent allows for the following events:

  • Login
  • Logout
  • ForceBuild
  • AbortBuild
  • StartProject
  • StopProject
  • CancelRequest
  • SendMessage

This covers all the current events that occur in security – later this list will probably expand.

Next, to actually log an event I modified CruiseServer to record events. Since CheckSecurity() is an important method, I added an eventType parameter to the args. Then, based on the result the correct event log can be logged. I also modified this method to log the correct type of event. If the args for LogEvent() are known, then these are added – otherwise they are set to null.

To record login and logout events I modified SessionSecurityManager. These events are logged in the Login() and Logout() methods – mainly because these are the points where the user names are known.

Logging the Event

After I modified ISecurityManager, I modified NullSecurityManager and SessionSecurityManager to include the new method. Initially these changes didn’t actually do anything – for NullSecurityManager it will remain this way. But for SessionSecurityManager I actually want it to do something. To handle this I added a new interface – IAuditLogger. This has the same LogEvent() method as ISecurityManager, but it is where the events will be actually logged. The idea is people can write implementations of this interface to log to whatever logs they want.

Next I’ve added a property to SessionSecurityManager that gets/sets an array of IAuditLogger - which has a ReflectorProperty of audit. Now admins can configure their servers to include as many or as few audit loggers as they like (by default it is an empty array).

Finally, I implemented the LogEvent() method in SessionSecurityManager. This merely iterates through the array and calls LogEvent() on each audit logger.

The Actual Worker

Now it’s time to actual log some audit events.

To help with future logger I’ve added a base class called AuditLoggerBase. This has two reflected properties – LogSuccessfulEvents and LogFailureEvents. This is to allow which events should be logged (later I might expand it to have more fine-grained control). I also added the LogEvent(). This implementation checks the two properties and if required calls a protected abstract method called DoLogEvent() – yes, this is the method to perform the actual logging.

My first implementation of this class is FileXmlLogger. This inherits from AuditLoggerBase and implements FileXmlLogger. It has a ReflectorType of xmlFileAudit, so it can be added to the configuration. It has a reflected property – AuditFileLocation to allow configuring the location of the audit file. This will default to “SecurityAudit.xml”.

Finally I implemented DoLogEvent(). This generates an XML document and adds all the available informaton. This is then written to the audit file – simple.

Where to From Here?

This now includes the basic ability to log security events. However, it is just that – basic. There are a number of enhancements that I can think of, which I might do sometime. These are:

  • Additional loggers (e.g. e-mail, event log, XMPP, etc.)
  • Additional log formats (e.g. plan text, etc.)
  • An ability to read an audit log
  • More fine-grained control over which events are logged

But, other than these, it is working nicely :)

Posted in CruiseControl.Net, Security | Leave a Comment »

Security Enhancements 2: Active Directory and File-based Sessions

Posted by Craig Sutherland on 9 October, 2008

Extensions

A lot of my work in building security for CruiseControl.Net has been in making it extendable. Rather than forcing people to use only one form of authentication, or authorisation or caching, I’ve built a framework that people can add their own extensions in to (very similar to the underlying principal of CruiseControl.Net in general). Now is the time to put this to the test and try and build a couple of extensions.

One common scenario for security is in a local security domain that uses Active Directory (AD). It would be nice to allow this rather than forcing people to use another user name/password. So I’m going to add an authentication process that uses active directory.

Secondly, I don’t like the way sessions are lost when the server is restarted (e.g. when the configuration changes or the service does a restart). It would be nice to have a more persistant session cache that handles restarts. Since the cache management is also extendable, I’m going to add an implementation of a file-based session cache.

AD Authentication

An authentication process needs to implement IAuthentication, which also includes ISecuritySetting. ISecuritySetting returns an identifier which is used by the security manager for storing the authentication. IAuthentication contains a method for authenticating, plus methods for returning the user and display names. Additionally, since these are loaded by Exortech.NetReflector the class must have the relevant attributes to allow Exortech.NetReflector to load it.

I’ve added a new class called ActiveDirectoryAuthentication that implements IAuthentication. This has a ReflectorType attribute of “ldapUser”, and a ReflectorProperty decorated property called UserName. This property will be where either the AD name or a wild card will be stored. I also added another decorated property called DomainName for storing the LDAP domain to use – this is also a required property.

Now the fun part comes with the Authenticate() method. This will be responsible for checking that the credentials are actually valid. To handle finding a user account, I’ve added a private method called FindUser(). This uses LDAP to search for a user, and if found returns the user name. The Authenticate() method uses this to find a display name. If there is a display name, then the account is valid, otherwise authentication has failed.

The GetUserName() method merely returns the name from the credentials, while the GetDisplayName() uses the FindUser() method to find the user. If a user is found, then the name is returned, otherwise the user name is returned instead.

And that’s all I’m doing for the AD authentication. Later I may look at adding password validation as well, but I’m not too keen on sending a password across an open network (I should also look into securing the transport mechanism as well).

File-Based Sessions

Session caching is exposed via the ISessionCache interface. This is a bit more complex than authenticate as it needs to store, remove and retrieve sessions, store and retrieve values against a session, plus it allows for an initialisation process. Now, I could write an entire session cache from scratch, but I’m going to cheat and base my cache on the InMemorySessionCache. To do this, I’m going to do some refactoring first and split the base functionality of InMemorySessionCache into an abstract base class and then I’ll implement my file-based session cache from this base class.

The reason for this is I’m not too sure on how Exortech.NetReflector will handle a type that inherits from another reflector type – so I’m going to play it safe. For my refactoring all I did was move al the functionality into its own class, made all the private entries protected and made sure all the public entries were virtual. The new implementation of InMemorySessionCache is just an empty class that implements my new abstract base class, plus it contains the RelectorType attribute (since all the memory cache functionality is in the base class).

Now for the fun part – I’ve added a new class called FileBasedSessionCache. This is also derived from SessionCacheBase, but it also needs to persist data to the file system. I’m going to use the basic memory-based caching so the file system isn’t getting overworked, but I also need to update the file system whenever new details are set. So, the methods that update the file system are:

  • AddToCache()
  • RemoveFromCache()
  • StoreSessionValue()

I’m also going to override Initialise() to load all the data from the file system.

For saving session details I’ve added a SaveSession() method. This will persist all the session details into an XML file. There will be one file per session – this is for performance reasons (a single file would mean reading, parsing and writing a single file every update). I’ve added a new property, StoreLocation, to store where these files will be stored. By default this is a folder under the current directory called sessions. This is exposed via Exortech.NetReflector so the administrator can change it if required.

All actually saving of the file is done by a helper method called SaveSession(). This does the following:

  1. Generate the file name (by another helper method – GenerateFileName())
  2. Check if the file exists
  3. If the file exists, load the file and parse it into an XmlDocument
  4. If the file doesn’t exist, start a new XmlDocument, add a root node and store the session token
  5. Store the user name in the document
  6. Add all the values
  7. Save the document

AddToCache() and StoreSessionValue() merely call the base methods, and then the SaveSession() method. RemoveFromCache() again calls the base method, but instead of saving it attempts to find the file and delete it.

The only outstanding method is Initialise(). This first checks fr the existance of the directory. If it doesn’t exist then the directory is created. Otherwise it lists all the directory files and attempts to load them. This involves loading the session into an XmlDocument, extracting all the relevant values, reconsituting the session entity and adding it to the base cache. Simple :-)

End of Changes

That’s the end of the changes – the security framework has now been extended to handle a new authentication mode and a new cache mode. Hopefully this shows how easy it is to add new extensions to the security.

Posted in CruiseControl.Net, Security | Leave a Comment »

Security Enhancements 1: Display Names

Posted by Craig Sutherland on 8 October, 2008

What’s in a Name?

One of the common uses for security is to retrieve a user name. Since the user has been authenticated, the system can guarentee the person’s name – as opposed to an unsecured system where the name can be anything the client sends.

While the user name is being set and used internaly, this isn’t being passed on to methods like ForceBuild(), AbortBuild() or Request(). It would be nice to use the secure name where possible to guarentee the right name is displayed.

Types of Names

The first issue is display names vs. user names. A user (or login) name is the name that is used for logging in – this will often be a unique name that could also be a code. For example my user name at the university where I used to work was csut017 (also the code for this blog). In contrast my display name was Craig Sutherland – a nice friendly name, but not a guarenteed unique one.

Now I could just use the user name for the display name, but it would be nice to have some friendly names. So the first part of my modification will be to add display names to the authentication. Once I’ve done this, I’ll then look at passing on these display names to the various methods that require a name.

Getting the Display Name

Currently the interface responsible for retrieving the user name is IAuthentication. This has a method – GetUserName() – that will retrieve a user name from a set of credentials. So my first change is to modify this method to also retrieve a display name. This will also take in the credentials and return the display name.

Currently this interface is implemented by UserPasswordAuthentication and UserNameAuthentication. Both these implementations will have a similar process for retrieving the display name. I’ve added a new property called DisplayName that is exposed via a ReflectorProperty attribute (“display”). If this is set in the configuration then it will be returned as the display name, otherwise the user name will be returned.

This will handle the named user scenario, but it won’t handle wildcards. However, since these are both very simple authentication processes, I’m not going to worry about them. More complex authentications can implement different versions of this method.

Storing the Display Name

How that the display name is available it needs to be stored somewhere. This is because credentials are not stored on the server – instead a session is generated and stored, and a session token is used in all future calls.

The interface for caching is ISessionCache. Rather than hard-coding a display name into the cache, I decided to allow multiple values to be stored in a session. To handle this I’ve added StoreSessionValue() and RetrieveSessionValue() methods. These require a session token and a key for the value. The values will be an object to allow for a wide range of possible values.

The only implementation of this interface so far is InMemorySessionCache. Internally this has a struct for storing session details. It is an easy enough task to add a dictionary to this struct that can be used for storing values. The two new methods just use this dictionary internally.

I also did a bit of refactoring. First I changed the struct to a class (although still private). Secondly I added a private method to retrieve an instance of this class from the cache. This also handles expirations – saves me having to copy and paste this functionality with minor changes for the session values.

Now that I have somewhere to store the values, I modified the Login() method in SessionSecurityManager to also store the display name in the cache if the login is successful.

Passing on the Display Name

The final step in this process is to pass the display name onto the required methods. Since the security is handled by the ISecurityManager interface I added a method to allow retrieving the display name from here (yes I decided to hard code it here since it will always be used). ISecurityManager already has a GetUserName() method to retrieve a user name from a session token, so I just added a similar method but for the display name.

ISecurityManager is implemented by NullSecurityManager and SessionSecurityManager. NullSecurityManager always returns null (more on this soon), while SessionSecurityManager will attempt to retrieve a value from the cache. If one is found this will be returned, otherwise null will be returned.

In CruiseServer I modified CheckSecurity() to retrieve the display name if all the checks succeed. This display name is returned from the method so it can be used by the callers.

Finally I modified ForceBuild(), AbortBuild() and Request() to pass on the retrieved display name. If this name is not null or empty, then the received user name is changed to the display name (hence the reason for returning null earlier). And now the secured display name is used for the display name :)

As an added bonus, dashboard requests (either directly or via HTTP protocol from CCTray) will now display an actual user name instead of ‘Dashboard’.

Coming Up

I still have a few idea of additions for security that I’m going to try and do before I go on holiday. These ideas are:

  • Audit logging
  • AD authentication
  • File-based session cache

Plus some more work on the installer and a couple more ideas (which need some more thinking first). I’m not sure how much I’ll be able to do, but if you have any ideas of what could be included let me know and I’ll try and make some time for it.

Posted in CruiseControl.Net, Security | Tagged: | Leave a Comment »

Security Issues – Fixing Round #2

Posted by Craig Sutherland on 7 October, 2008

Round #1 Recap

In round #1 (read it here) I looked into the first four issues on my problem list.

Issue #1 involved adding some documentation. I imagine this will be an ongoing process, but for now I have an easy place to go to review security configuration options.

Issue #2 involved adding some more security implementations – to starting and stopping projects, plus adding a default right to the current assertions.

Issue #3 was merely adding a new attribute to show user-friendly display names.

Issue #4 was what stopped me. For WCF and .Net Remoting it appears to work fine, but HTTP consumes the error in the dashboard.

Handling HTTP Exceptions

Since the dashboard handles the exception, CCTray doesn’t get any error details through. However it does get a page response back, and embedded in this page are the exception details.

Therefore to get around this problem I’ve added a new method called HandleResponseAndThrowExceptions(). This simply does a string search through the response for any of the security exceptions and then throws the expected exception. Not the nicest way, but it now allows CCTray to show the correct error when a problem occurs.

Issue #5: Timed-out Sessions

Now that CCTray gets exceptions whenever an action fails, I can start to look at handling session expiries. The basic problem is a security call is only made for actions like force/abort build or start/stop project, checking the project status isn’t counted as a security call. Therefore for sessions which have an expiry time (they all do and by default this is set to ten minutes), there is a good chance the session will time out before a user tries a security action.

Now I thought of two basic ways around this problem:

  • Make the project status a security call
  • When a security call fails with a session timed out, re-login and try again

In the end I decided for the retry approach. This was because of the huge number of project status requests (one request per project every 5s with the default settings). To make this a security request will add a huge amount of overhead to the server! Whereas the retry approach means doing the same call twice, but as these calls are less frequent, the overall workload is less.

Looking at the code, the simpliest place to add the retries is ProjectMonitor. This is the step before the actual transport-dependant calls are made, so it means changing only one place instead of 3+. The basic series of steps is:

  1. Attempt the call in a try-catch block
  2. Catch any SessionInvalidException errors
  3. If an error is caught, do a refresh session call and retry the call

Since there are (currently) six methods that use security I’ve taken advantage of annoymous methods to do the processing. I’ve writed the above steps in a method that takes in a delegate. Then each time I need to do an actual action I call the delegate.

The method to handle the retry is as follows:

private void AttemptActionWithRetry(ActionHandler actionToTry)
{
    try
    {
        actionToTry();
    }
    catch (SessionInvalidException)
    {
        // Let's assume the session has expired, so login again
        if (this.serverMonitor.RefreshSession())
        {
            // Now retry the action again
            actionToTry();
        }
    }
}

While to call the method I use the following:

public void ForceBuild()
{
AttemptActionWithRetry(delegate()
{
cruiseProjectManager.ForceBuild(serverMonitor.SessionToken);
});
}

I did the same for all the methods that take in session tokens.

The final piece to make this work is the RefreshSession() method. This is a new method that I added to ISingleServerMonitor. This merely does a Logout() and then a Login(). It also returns the result of the Login() – either true or false. The Logout() is just to ensure the local session token is cleared.

Issues Resolved

That’s the first round of issues resolved. Time to go back to testing :-)

Posted in CruiseControl.Net, Security | Tagged: , | Leave a Comment »

Security Issues – Fixing Round #1

Posted by Craig Sutherland on 6 October, 2008

Let’s Fix Some Issues

In a previous post (here), I mentioned a number of issues I found while testing. During writing that post I found five issues that made using the security in CruiseControl.Net. Since I’m the one who wrote it, I shouldn’t be having a hard time, but yes, I did find issues :-(

In this post I’ll start to look at the different issues and how to resolve them.

Issue #1: Documentation

Yes, my documentation on the original work was sorely lacking. While I documented most things from a technical viewpoint, there was no clear and easy-to-understand documentation on how to configure security on the server.

I’ve now gone through and added some articles on how to configure the documentation. These articles can be found here.

As I stated, this is my short-term resolution. When the security work is moved into the trunk I’ll get these added to the main documentation for CruiseControl.Net.

And in the long term I’m going to build a security settings tool (or maybe get it added to CCNetConfig).

Issue #2: Complete Implementation

My second headache was discovered I had mentioned things I was planning on doing in my posts, but I hadn’t actually done them yet! These basically fell into two areas:

  • No default right for the assertions
  • No implementation for starting/stopping a project

The default right is merely a matter of modifying AssertionBase. This is the class that contains all the security rights – so I added the default right, decorated it with the correct attributes and then modified CheckPermissionActual() to return this right instead of SecurityPermission.Inherit. Now a default right can be set for each assertion.

The start/stop implementations were a it more work. This involved first modifying ICruiseServer/CruiseServer to allow passing in a session token. I also added the session token to CancelPendingRequest() and SendMessage() just to be thorough.

From here I also had to modify ICruiseManager/CruiseManager. This again involved adding another override to take in a session token for each of the secured methods. The old methods are also there, but they pass in null for the session token.

Since these methods are called by both CCTray and the dashboard, these applications also needed changing.

In CCTray the starting point is ICruiseProjectManager, since this is the interface that handles the communications. This required modifying HttpCruiseProjectManager, RemotingCruiseProjectManager and WcfProjectManager (in the WCF extensions) to handle the secured methods. Finally ProjectMonitor was modified to pass in the session tokens.

In the dahboard the starting point is IFarmService. This only has two methods to be modified – Start() and Stop().  This required modifying ServerAggregatingCruiseManagerWrapper to match (just passed on the session token) and then finished with VelocityProjectGridAction - getting it to pass in the session token to the modified methods.

That is now all the update actions secured. Not sure about whether there is a need to secure read-only actions, so I’m going to leave these for now.

Issue #3: Friendly display names

The next issue is reasonably simple – there is no friendly displayname for each extension. Instead each extension has the full .Net assembly name (not very nice, especially as they are too wide for the display). To get around this I have added a new attribute – ExtensionAttribute. Currently this has only one property – DisplayName, but later I’m planning on adding more.

When I load the list of extensions I call a helper method in ExtensionHelpers - CheckForDisplayName(). This loads the specified type, looks for the new attribute and if found returns the DisplayName property. If there is no attribute, or the DisplayName property hasn’t been set, then it returns the full assembly-based type name.

This code is called from ConfigureServer - which is what currently displays the list of security extensions. I had to do a bit of playing around to get it working nicely (since comboboxes don’t have name/value pairs for their items). This involved adding an internal struct to hold the extensions and their display names.

Now I can see some nice friendly names:

Improved server configuration
Improved server configuration

The authentication modes can now easily be distinguished :-)

Issue #4: No Warnings

I not sure why this is happening, but looking at my code I saw that the exceptions were defined in both Core and Remote, which might be causing problems. I’ve taken them out of Core so both CCTray and the server should be using the same settings.

However, even so when I try it the exceptions don’t come through :-(

So I did some more investigations. Currently I have three different communications protocols – HTTP, .Net Remoting and WCF. It turns out the problem one is HTTP – obviously the error is being trapped somewhere and not passed on. .Net Remoting and WCF both tell me there is a problem – although WCF doesn’t tell me what the problem is!

Time For a Break

That’s enough problem-solving for the moment. I’ve tracked down where the issues are for the non-displayed warnings, but as it’s going to take a while to fix the HTTP problem I’m going to leave it for later.

Since issue #5 depends on issue #4 I’m also going to leave this for later.

But I have resolved some of the issues, so if you want to try it, download the code from the security branch and give it a spin :-)

Posted in CruiseControl.Net | Tagged: , | 5 Comments »

Wix and CruiseControl.Net

Posted by Craig Sutherland on 5 October, 2008

An Introduction to Wix

I guess the first thing I should do in this post is introduce Wix. For those who haven’t met Wix before it stands for Windows Installer XML. Basically it’s another open source tool for building MSI packages. The main difference between this and some of the other tools (e.g. Nullsoft) is it is entirely XML-based. Since MSI packages are based on internal tables, not setup scripts I think the two go well together.

Now I tried to take a quick look at the existing installer in CruiseControl.Net, but I couldn’t find where it is. Since I took the easy approach and gave up and changed to something that I’m used – Wix.

A Newer Installer

For those of you who are wondering why I’m doing this in the first place, I wanted to make it easy for people to try my new extensions to CruiseControl.Net (WCF and security). The idea being if I can make it easy for people to install, then hopefully people will start using it and give me some feedback on how I can improve it.

So, what is in my new installer. First, there are the standard components:

  • The console and service versions of CruiseControl.Net (the actual heart of the system)
  • CCTray
  • Web dashboard

Next I’ve added my WCF extensions:

  • WCF extension for the server
  • WCF custom transport for CCTray
  • WCF router

Finally I added in CCCmd (my command-line interface to CCTray) to allow people to use non-UI tools (e.g. scripting languages, etc.)

In case you’re wondering where the security is, it’s been built into the standard components. People can turn off security, but it cannot be removed. In future releases of CruiseControl.Net it will be in the main product line, but I don’t consider it ready yet to be added into the trunk.

I’ve also spruced up the interface a little – I’ve given a new logo and splash banners in the installer. I’ve also included the standard license. People have a choice of typical (just the standard components), custom or full installs – so they can choose exactly what they want.

I’ve got the service installation working, but I’m having problems with the IIS registration (not sure if it’s my machine or something else).

Where is it?

For the moment I’ve added the Wix code to the security trunk (we can discuss what to do with it in the long term later). I have also loaded it to my google repository (here) so it will be available for general access.

Please let me know any feedback on it, or on any of the WCF/security work. I should mention that this is a work in progress – I hope to smarten it up later on and add some nice functionality to it (more on that to come).

Posted in CruiseControl.Net, Security, WCF | Tagged: , | Leave a Comment »

WCF and Security

Posted by Craig Sutherland on 4 October, 2008

Revisiting WCF

Back when I got involved in CruiseControl.Net, the first thing I added was the ability to extend the server with some extensions, and also the ability to add custom transports to CCTray. The purpose of these extensions were to allow a WCF transport mechanism. As we didn’t want to force an upgrade of CruiseControl.Net to .Net 3.5, this was done as a set of separate libraries that could be added if desired.

Since this original work I’ve gone on to add security to all of the CruiseControl.Net components. While this work was based on the extension version of the projects, I didn’t modify the actual WCF libraries. In this post I’ll write about the changes that were necessary to get WCF working nicely with security.

Server Changes

The only interface that a server extension needs to implement is ICruiseServerExtension. This has nothing to do with security (or communications at all). All it does is provide methods for allowing CrusieServer to initialise, start and stop/abort the extension. So no changes required here.

The transport is actually handled by ICruiseControlContract and CruiseControlImplementation. Since these types handle communications from a client, they need to handle login and logout, plus pass the session token through. Therefore I added some new methods to handle the login and logout which are just pass-through methods. I also extended ForceBuild() and AbortBuild() to take in a session token.

To handle the security credentials I also added a new class to pass the credentials (since this needed to be decorated with WCF attributes).

That was all the server-side changes. I compiled it and it ran nicely :)

CCTray Changes

A custom transport is required to implement ITransportExtension. This extension returns instances of ICruiseProjectManager and ICruiseServerManager - both of which were modified by the security changes. Therefore my first changes were to modify the implementation of these two interfaces – WcfProjectManager and WcfServerManager. These changes were similar to the server changes (e.g. login/logout methods, plus passing the session token).

However the other changes were a little more stuble – ICruiseServerManager no longer uses an URL, instead it takes in the whole BuildServer configuration. Therefore I needed to modify WcfServerManager to store the entire configuration as well. This then required modifying all the places that were using the URL, but still pretty simple.

The other change was harder – I needed to convert a ISecurityCredentials into a set of SecurityCredential instances (this is the new data contract class I had added to the server extension). The problem came from there being no way of retrieving all the credentials from ISecurityCredentials. This required a modification to this interface (plus its implementation – UserNameCredentials). Once this was done it was a simple matter to convert the credentials over and pass them through.

End of Changes

And that’s it, my security changes didn’t break the WCF extensions too much at all. The WCF router (an optional part of the dashboard) is completely unaffected as it is just a pass-through layer.

So now I can have both WCF and security on my CruiseControl.Net environment :)

Posted in Security, WCF | Leave a Comment »

Securing CruiseControl.Net – Web Dashboard (Part IV: Completion)

Posted by Craig Sutherland on 1 October, 2008

Previously

In my last post (here), I had added authentication and sessions to the web dashboard. While authentication is an important part of security, it is only half the story. The other half is authorisation. Previously I had modified the server components (Core, Service and Console) so they used a session token to identify a user and then work out whether they had permission to perform the action. So, to complete the web dashboard modifications I need to pass the session token to the force/about build method calls.

Where to Start

While there are a couple of possibilities, the place that I’m going to start is with IFarmService. This is the interface that is responsible for passing server requests along. Previously I had modified it so the ForceBuild() and AbortBuild() methods took in a session token parameter. Now I need to find the places where this is actually called and pass in the valid session token.

These methods are called in two places:

  • ForceBuildXmlAction (ForceBuild() only)
  • VelocityProjectGridAction

Now both of these places I have modified previously to accept a token from a CCTray client. Now I need to modify VelocityProjectGridAction to also look for a token in the query string (since ForceBuildXmlAction is an XML action I don’t need to modify it again).

To do this I already have an interface defined – ISessionRetriever. So first change is to modify the constructor to request an instance of ISessionRetriever and store it locally. Then I modified ForceBuildIfNecessary() to first check the request for a session token (previous change), then if not found to use the session retriever.

And that’s all there is to it. Now a user can login and force/abort a build (assuming they have permission).

Session State

Previously I had added session state by storing it in the query string. While this works for a single machine it doesn’t handle multiple remote servers. Additionally, while I’ve stored the session token in the query string, others might want to store it in a different place (e.g. cookies, file system, etc.) or even not at all (e.g. if using integrated security).

To get around this I’m going to modify the session state mechanism so it is configurable. Already I’ve made the login mechanism configurable, now I’m going to expand the configuration to add a session store. To do this I’ve added a new interface called ISessionStore. This has two methods: RetrieveStorer() and RetrieveRetriever(). This methods merely retrieve an instance of ISessionStorer and ISessionRetriever - it is up to the session store implementation to generate and return these.

Next step was to modify NetReflectorPluginConfiguration to allow an administrator to set an ISessionStore. I’ve added property called SessionStore, plus its backing field. This defaults to an instance of QuerySessionStore (my default implementation of ISessionStore). This has been decorated with a ReflectorProperty attribute, and since it is a singleton I’ve set InstanceTypeKey to be “type”. Now it can be automatically loaded.

I also modified IPluginConfiguration since this is the interface that is used. Finally I modified SetupObjectSourceForRequest() in CruiseObjectSourceInitializer to use the new SessionStore property in IPluginConfiguration. And now it is possible to change the session store mechanism – or at least that’s what I thought!

The problem is the order in which the configuration and url builders are loaded – the url builder needs to be loaded first, followed by the configuration. But the session storer is needed to load the url builder, and this needs the configuration to be loaded. I got around this problem by making the ISessionStorer a property on DefaultUrlBuilder (instead of a constructor argument) and manually setting after the configuration has been loaded.

And now it works :-D

Security Completed

That now completes my first iteration of security. I have added security to all the components of CruiseControl.Net and in a way that is configurable and expandable. While it works, I know it still needs refining and that’ll be my next task.

However if you want to play with it, it is now ready and can be tested ;-)

When I have some more time in future I’ll start refining the security components so they are more user-friendly. I’ll also add some more implementations of different security components. And not to mention documentation!

Posted in CruiseControl.Net, Security | Tagged: | 3 Comments »