Archive
Security in CruiseControl.NET
From CruiseControl.NET 1.5 onwards there is the option to configure security. Unfortunately due to some limitations in the way it was implemented it is only always easy to figure out how to do it. Recently there was a question about why are the projects not visible in the dashboard after security was added. In this post I’ll go over why the problem occurred and some possible ways to fix the problem.
Here is an example configuration that demonstrates the problem:
<cruisecontrol xmlns="http://thoughtworks.org/ccnet/1/6" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<internalSecurity>
<users>
<passwordUser name="Bob">
<display>Bob the Builder</display>
<password>Bob1</password>
</passwordUser>
</users>
<permissions>
<rolePermission name="Builders">
<forceBuild>Allow</forceBuild>
<defaultRight>Deny</defaultRight>
<users>
<userName>
<name>Bob</name>
</userName>
</users>
</rolePermission>
</permissions>
</internalSecurity>
<project name="SecurityTest">
<security xsi:type="defaultProjectSecurity">
<defaultRight>Deny</defaultRight>
<permissions>
<rolePermission name="Builders" ref="Builders" />
</permissions>
</security>
</project>
</cruisecontrol>
Here there is an user called bob the Builder who has forceBuild permission – all other rights are denied. The problem is there is another right called viewProject which controls the visibility of projects in the dashboard. Since all other permissions are denied this permission also is denied, so the project does not appear in the dashboard!
Now if we are happy only allowing Bob the Builder to see project then the solution is easy – add a viewProject right and set it to Allow:
<rolePermission name="Builders">
<forceBuild>Allow</forceBuild>
<viewProject>Allow</viewProject>
<defaultRight>Deny</defaultRight>
<users>
<userName>
<name>Bob</name>
</userName>
</users>
</rolePermission>
Now when Bob the Builder is logged on he can see the project. But what if we want to allow everyone to see projects?
To allow this we need to add a guest account with the view permission as well:
<cruisecontrol xmlns="http://thoughtworks.org/ccnet/1/6" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<internalSecurity>
<users>
<passwordUser name="Bob">
<display>Bob the Builder</display>
<password>Bob1</password>
</passwordUser>
<simpleUser name="Guest" />
</users>
<permissions>
<rolePermission name="Builders">
<forceBuild>Allow</forceBuild>
<viewProject>Allow</viewProject>
<defaultRight>Deny</defaultRight>
<users>
<userName>
<name>Bob</name>
</userName>
</users>
</rolePermission>
</permissions>
</internalSecurity>
<project name="SecurityTest">
<tasks>
<commentTask>
<message>Ran successfully!</message>
</commentTask>
</tasks>
<security xsi:type="defaultProjectSecurity">
<defaultRight>Deny</defaultRight>
<guest>Guest</guest>
<permissions>
<userPermission user="Guest">
<defaultRight>Deny</defaultRight>
<viewProject>Allow</viewProject>
</userPermission>
<rolePermission name="Builders" ref="Builders" />
</permissions>
</security>
</project>
</cruisecontrol>
This example adds a new account called Guest, sets it as the guest account for the project and gives the account view permission. Now everyone can see the project in the dashboard.
Note: the Builders role still needs the viewProject right – otherwise when Bob logs in he will no longer be able to see the project!!
Security Documentation
A while ago I wrote about some scenarios for security. These were written as I was in the process of implementing security for CruiseControl.NET, so they were up-to-date at the time.
However, as I neared completion of security, I realised some things were not as clear as they should have been, so I changed a few things. These changes unfortunately broke the scenarios – in that the configuration that was produced was no longer correct
Now, as part of the drive to update the documentation for CruiseControl.NET, I have revised and corrected these scenarios. Each scenario is based around a fictitious company or project and goes through the whole process of designing and implementing the security for that scenario.
They were designed to give an overview of what can be done with security, although they don’t cover all the possible scenarios (feel free to provide some more examples and I’ll write them).
Anyway, here are the updated scenarios:
Hopefully these will help in implementing security for CruiseControl.NET.
The Missing Piece: Encrypted Communications
Introduction
In my last post (read it here) I wrote about an issue with security – that the actual communications between the client and the server were not secure. I also outlined the approach I was taking to remedy the issue.
Before I move onto my solution, I must mention that this is not a perfect solution. As one of my readers commented, this does not really cover a man-in-the-middle attack. It will prevent network sniffing and replay attacks, it is still possible to insert a listener between the client and the server.
It is possible to configure .NET Remoting to intrinsically secure communications – in which case there is no need to worry about this from the client side at all. However, it is not easy easy as it might appear, so I decided to finish my partial solution.
In time, I hope to be able to look at expanding the communications channels. Currently the only channel for communicating with the server is .NET Remoting – which was because of the way methods used to be called. Now with the new messaging-based framework, it is easier to add new channels. Hopefully I will get some time to look at using SslStream and NegotiateStream to secure communications.
Client Communications
First, how does the new code send encrypted messages to the server? This involved expanding how messages are currently sent.
Currently messaging uses the following model:
Using the new communications model, each client application will generate an instance of a CruiseServerClient. Actually they generate a CruiseServerClientBase instance, but the class that implements this abstract class for the new framework is CruiseServerClient. This class does not actually send any messages – instead it translates the method calls (e.g. ForceBuild) into messages (e.g. ProjectRequest), sends the message and then processes the response.
The actual sending is done by an instance of IServerConnection. This instance takes the specified messages and pushes them over the specified channel (either .NET Remoting or HTTP). Because the actual channel is encapsulated by an IServerConnection, it is easy to replace the channel with a different one.
For encrypted communications I added a new channel called EncryptingConnection. This channel is then inserted between the client and the underlying communications channel:
This means that the messages are encrypted (both ways), but the underlying transports (e.g. HTTP or .NET Remoting) can still be used without any changes.
There are a couple of extra changes. First, the original request message gets replaced by an EncryptedRequest message. This contains the original message, but encrypted (of course), and the original action that was requested. Secondly, the action has been changed to ProcessSecureRequest – more on this when I cover the server side changes.
EncryptingConnection is also responsible for the hand-shaking required to set up the encrypted communications. When a message is sent, the connection checks to see if it has a password. If it does not have a password, sets up a new common password by the following process:
- Ask the server for its public key
- Generate a new secret (using RijndaelManaged)
- Encrypt the secret using the server’s public key (using RSACryptoServiceProvider)
- Send the secret to the server and wait for the response
- Store the secret locally
Once this has been done, all messages are then encrypted using the common secret (again with RijndaelManaged). This includes both request and response messages.
The actual sending of the messages is then done by passing the message onto the underlying channel.
Server Communications
Now that the client can send messages, we need to look at the server side. After all, if the server can’t understand the messages, then nothing will be done!
All communications into the server come in via CruiseServerClient. This is a MarshalByRefObject-derived class that uses .NET Remoting. All HTTP communications actually go via the dashboard, which passes them onto the server using .NET Remoting. So this model simplifies things – I only need to change CruiseServerClient!
Currently CruiseServerClient has one overloaded method for processing messages – ProcessRequest(). This takes the incoming message, works out which method to call, converts the message into the required objects (if required) and then calls the method. It also handles generating the response and adding any error information.
To handle secure messages I have added a ProcessSecureRequest (). This method is called by ProcessRequest() and acts as a middle-man. Basically, when ProcessRequest() gets a secure message, it passes it onto ProcessSecureRequest(), which then works out which methods to call, converts the message, etc.
I have also added three other new methods:
- RetrievePublicKey() – this will send the server’s public key to the client. If the server does not have a public/private key pair it will generate the pair (using RSACryptoServiceProvider).
- InitialiseSecureConnection() – this receives the common secret from the client, decrypts it and stores it locally.
- TerminateSecureConnection() – this will remove the common secret for a client from the local store.
These methods are called as part of the hand-shaking between the client and the server.
Additionally, as part of these changes, I have added a new property to the messages called ChannelInformation. This property has information on how the message was transported – i.e. metadata about the message itself.
Currently, there is only one implementation – RemotingChannelSecurityInformation. This says the messages was transported using .NET Remoting. On this class is a property called IsEncrypted. ProcessRequest() generates an instance of this class and sets IsEncrypted to false, while ProcessSecureRequest() generates an instance with IsEncrypted set to true.
Turning On Encryption
Encryption is turned on from the client-side. When a client wants to communicate with a server using encrypted communications, they need to set UseEncryption to true in ClientStartUpSettings.
Currently, this is not possible with the dashboard, but I have added it to CCTray. When a new server connection is added (for either .NET Remoting or via the dashboard), there is a new option of turning on encryption:
To use encrypted communications, all that is needed is to check this box.
Unfortunately, due to the way CCTray is configured, it is not possible to turn on encryption for an existing server. Instead the server needs to be removed and re-added (yuck!) This is probably a good area for tidying up in a future release of CCTray.
Enforcing Encryption
All this work so far has been to set up encrypted communications. At this point, it is up to the client to decide whether the channel is encrypted or not – the server doesn’t really care. We need some way of telling the server to only accept encrypted communications (i.e. to enforce encryption).
To do this, I have added a new configuration option to the server security settings – channel. This is an optional setting and has one required property – the type of channel. The following shows how to configure a channel to use encrypted messages:
1: <internalSecurity>2: <channel type="encryptedChannel"/>3: <users>4: <!-- Omitted for brevity-->5: </users>6: <permissions>7: <!-- Omitted for brevity-->8: </permissions>9: </internalSecurity>This tells the security manager it will only accept communications over an encrypted channel.
When a message is received by the message processer (i.e. after it has been received by CruiseServerClient, processed and passed on), it calls a method called ValidateRequest(). This does some simple validation checks (the message is less than a day old and it hasn’t been duplicated). I have expanded this validation check to detect a channel definition and if present to validate it.
The Basics Of Encryption
This provides some of the basic pieces for encrypted communications. It certainly is not a comprehensive solution – there are still some areas uncovered (like validating the server is who it says it is).
In terms of where this can go, there are the following items that can be included:
- Additional encryption mechanisms (e.g. DES, triple DES, AES, etc.)
- Additional security channels (e.g. SSL, negotiated)
- Validation of the server (perhaps via X.509 certificates)
- Use encryption from the dashboard
But this provides a start. Let me know what you think so far.
One More Piece of the Puzzle
Security is Not Quite Finished…
Way back when I started on security for CruiseControl.NET, I wrote about a number of different areas that needed to be covered. Since then, I have completed most of them. Most, but not all…
One area that I originally wrote about was “man in the middle” attacks. To understand this attack, we need to step back first and look at the parts of CruiseControl.NET. CruiseControl.NET can be divided into two general parts: the server and a client.
The server is the heart of CruiseControl.NET, it performs the builds, checks the source control repository, etc. The client is a remote connection to the server, that a user can interact with – which can be either CCTray or the web dashboard. The client generally communicates with the server over a network:
Now, unless both the server and the client are on the same machine, this opens the possibility of a “man in the middle” attack. Generally, we assume that the message goes from the server to the client or vice-versa over the network without anything in between. But, this does not have to be the case. Instead, a third party can position itself in between:
In this scenario, all messages go through the third party, allowing potential malicious use of the system. Alternately, the third party could just monitor the traffic, such as with a network sniffer:
Either way, it is now possible for a third party to see and interact with the messages that are passed. As the messages could contain sensitive information (e.g. passwords and login credentials) or can control the server (e.g. start/stop projects, force/abort builds, etc.), we don’t particularly want a third party in between!
Now, people might say this is not an issue for them, but unless you can guarantee a secure network (or both the client and server are on the same machine), you are vulnerable to this type of scenario.
What Can be Done?
Before I look at what can be done, let’s look at why this attack can happen. When either the server or the client wants to communicate, they generate a message to send. This message is plain old text (well, actually XML, but close enough). This means there are open messages flying around the network:
Anyone can look at these messages and/or change them, and neither the client or the server would be any wiser. Actually, this isn’t just an issue with CruiseControl.NET, this is an issue with any communications over a network, like any HTTP traffic over the Internet. Any pages that are browsed via http:// are sent plain text. This is where https:// comes in – it secures the messages that are passed. The question is, how is this done?
The answer is very secure – each and every message is encrypted:
Since the messages are no longer plain text, any third party in the middle cannot read or modify them. A nice simple solution to the problem. But, like anything, things are never quite that easy!
In order for each side to understand the messages, there needs to be a common understanding – messages are using this form of encryption with this password. Now, with https:// the browsers handle this for you automatically (or almost automatically), so there is no need for user interaction. However, for CruiseControl.NET, nothing like this exists, so it will be my task to build it.
Reaching an Agreement
Now I’m not a cryptographer, so this post will simplify things (a lot!) But here is my basic plan of attack. For encryption there are two basic types – symmetric and asymmetric.
Symmetric is where both sides share a common secret (e.g. a password) and they both use this secret for encryption and decryption. Asymmetric is where there are two secrets – one is used for encryption and the other for decryption. A message encrypted with one secret cannot be decrypted with the same secret – it needs the matching secret to be decrypted.
This means one secret can be common knowledge – typically called a public key. Anyone can encrypt some data using the public key, and nobody can read that data unless they have the other secret – commonly called the private key. In our case, the server will have a public/private key pair. Any client can request the public key and use it to encrypt data to send to the server.
However, this plan has two problems. First, this only works for sending data to the server. Since anyone can access the public key, they could decrypt any data being sent back to the client. This can be resolved by giving the clients public/private key pairs as well, but now the server has a lot more work tracking which public key is needed for which client! The second problem with with asymmetric encryption in general – it is a more expensive (and hence slow) operation to perform than symmetric encryption.
There is a common solution to these two issues – combine the two types of encryption. When a client first connects to the server, it requests the server’s public key. Since this is public knowledge, there is no security hole. The client then generates a secret, encrypts it with the public key and passes it back to the server:
Since the password is encrypted with the public key, only the server with its private key can decrypt it – again no security hole. All subsequent communications between the server and the client then use symmetric encryption with the common secret – a much less expensive form of encryption. The server still needs to keep track of all the password, but it means the client doesn’t need both a public and a private key, so there’s a bit of a gain.
How Is This Done?
That’s the basic theory of things – you now know I’m going to implement encryption on the messages to secure them. This will prevent a third party from either reading or modifying any messages. This will use a public/private key pair on the server to set up a common secret between the two, with all subsequent communications using symmetric encryption.
The basic process will be something like this:
- Client requests public key from server
- Server sends public key
- Client generates secret, encrypts it with the public key and sends it to the server
- Server sends a confirmation
- Client and server then exchange messages with symmetric encryption
Nice and simple. But there are some catches:
- Not everyone will want to use this
- Someone can still pretend to be the server
- Needs to be extendable
Catch #1 can be set at both the server and the client sides, with the server having the ultimate say. If the server does not return a public key, then steps 2-4 will be skipped and the messages will be passed in plain text. The client can also skip straight to step 5 and just pass messages in plain text. However, the server will have a setting to enforce encryption – i.e. if the client attempts to send plain text messages and the server does not like them, they will be rejected!
Not sure on how to handle catch #2 yet – but I imagine this can be done by checking a hash. The server would generate a hash using its private key and the client would then decrypt it using the public key – thus verifying the server is who it says it is. This would require some external way of passing the public key though.
Catch #3 is fairly straight-forward – I will expand the server security configuration to include this. Each encryption method will be configurable – and people can add their own if they wish.
Anyway, that’s enough for tonight. I’ve got my general plan, in my next post I’ll go into details on how this will be implemented.
Stay tuned…
It’s Been a While
A Quick Update
I haven’t written a post for while now – not because I haven’t done anything, but because I’ve been focusing on getting a lot of my work into the trunk (that and my son is taking a lot of my time).
So here is a quick update on what has finally made it into the trunk.
1.4.4 Release
Some of my work was able to go into the 1.4.4 release. The following items have been included:
- Server extension points
- Simplified web administration
- Installation packages
- Package publisher
- NDepend task
- Extended source control exception handling
These changes have been added successfully and appear to be stable (let me know if there are any issues). Additionally I’ve also done a little bit of work on making the validator more useful (extended validation, its own installer, etc.)
These will all go out in the next release (hopefully soon!)
1.5.0 Release
This is the exciting one as security has finally made it! We did take a little step back and decided not to add either the messaging or communications client code as yet. Some of the security relies on this code, so it hasn’t been implemented yet (still pending a decision on whether we add this in the 1.5.0 release or wait for a future release).
Additionally I have added the build throttling extension and the task status break-down (just finished tonight).
I’m going to add the dynamic build parameters over the next week or so and then that will be most of my work finally committed to the trunk (with the main exception of messaging/communications client.) After this it will be a time of settling down and resolving any issues that come up (especially with security).
Visual Studio 2008
The other great news is we finally converted to Visual Studio 2008 for the solution and project files. This has both made my life easier (hence the new additions) and also allowed us to start using C# 3.0.
While we have not moved to .NET 3.5 yet, this is still a great step forward!
So a lot of work is happening behind the scenes and we are looking forward to a new release soon!
Hiding Projects
Adding Security to Viewing Projects
Most of my focus on security has been around preventing users from doing things (e.g. forcing and aborting builds, stopping and starting projects,etc.) However while working through my security scenarios, I put together a scenario where people might not want certain groups of users to view projects. And currently this isn’t handled by the security code!
But the good news is it won’t be too hard to implement. With the new messaging interface that I have added to CruiseControl.Net every message contains a session token. Using this token I can then check whether the user is allowed to see the project or not.
Now this is fairly simple for the requests that directly access a project, but it will be a little harder for messages that return a list. So, let’s jump into it and see what is required.
The Approach
The first question is, what methods need security around viewing projects? I had a look through ICruiseServer and the following methods return project information:
- *GetCruiseServerSnapshot()
- *GetProjectStatus()
- GetLatestBuildName()
- GetMostRecentBuildNames()
- GetLog()
- GetBuildNames()
- GetServerLog()
- GetProject()
- GetExternalLinks()
- GetArtifactDirectory()
- GetStatisticsDocument()
- GetModificationHistoryDocument()
- GetRSSFeed()
The two methods with an asterisk return multiple projects, so some way of filtering the projects will need to be added – the rest of the methods are for a single project.
So, step one will be to add the new permission and allow it to be set. Step two will be securing the single-project methods. Step three will be filtering the multi-project methods.
Step One: A New Permission
Permissions are defined in SecurityPermission. As this is an enumeration adding a new permission is very simple – just add a new enumeration value. The new permission is called ViewProject.
To set a permission in the configuration involves modifying PermissionBase. This class defines all the permissions that can be set in the configuration and maps them to SecurityPermission. This means adding a new backing field and property, and then setting the NetReflector attribute. This property I have called ViewProjectRight so it matches the rest of the properties.
The actual mapping between the property and SecurityPermission is done in the CheckPermissionActual() method. This is just a big switch statement that maps the request permission to the actual allowed permission.
Step Two: Securing Single Project Calls
The following is an example of how most project methods are implemented:
public DataResponse GetProject(ProjectRequest request){string data = null;DataResponse response = new DataResponse(RunProjectRequest(request,null,null,delegate(ProjectRequest arg){Log.Info(Resources.LOG_GetProject, arg.ProjectName);data = new NetReflectorProjectSerializer().Serialize(configurationService.Load().Projects[arg.ProjectName]);}));response.Data = data;return response;}The main code to note is a call to RunProjectRequest(). This has four parameters – the request to process, two parameters that are set to null in the example and a delegate that does the actual work.
Looking at the arguments for RunProjectRequest(), the second argument is the required security permission, the third is the audit type. Both of these are nullable parameters, as can be seen from the example code. To check a permission is just a matter of changing the first null to SecurityPermission.ViewProject:
public DataResponse GetProject(ProjectRequest request){string data = null;DataResponse response = new DataResponse(RunProjectRequest(request,SecurityPermission.ViewProject,null,delegate(ProjectRequest arg){Log.Info(Resources.LOG_GetProject, arg.ProjectName);data = new NetReflectorProjectSerializer().Serialize(configurationService.Load().Projects[arg.ProjectName]);}));response.Data = data;return response;}Now RunProjectRequest() will validate that the user has the required permission first before running the delegate.
So to lock down single-project requests is simply a matter of going through CruiseServer and setting this permission.
Step Three: Filtering Projects
To handle request that return multiple projects I have added a security filter. This filter will check a list of ProjectStatus instances and remove any instances that the user does not have permission to view.
I decided on this approach because it only requires a change at the CruiseServer level. While it would probably be more efficient changing it in the code that actually generates the list, this would require changing the code in two places and require passing the session token through a few more classes.
Because I don’t want to throw exceptions during the filtering I couldn’t use the default security checking method. Instead I extracted the relevant code and made a modified version that doesn’t throw an exception (or do logging). This method is also a bit faster since it only retrieves the user name from the session token once.
Projects Locking Completed
With this change the user now has the option of completely locking down a project – even to the point where the projects will no longer even be visible. This hopefully covers the full range of security options for projects
Let me know if I have missed anything.
Implementing Messaging, Part I
Nibbling a Little Bit at a Time
In my last post (here) I outlined my plans for changing to a messaging-style method invocation. This is purely for the client-server communications, and will hopefully make things easier in the long run.
Well, I have started on an implementation of these plans. There are a total of 37 different operations that can be called on ICruiseManager (some of which have different overloads, which brings the total up to around 50). Trying to do all of these in one sitting would be a lot of work, but make it harder to guarantee everything works! Therefore, given the huge scope I’ve decided to nibble a little bit at a time.
My plan, in broad strokes, is as follows:
- Implement a proof-of-concept using the start and stop project methods
- Convert all methods that were modified for security (e.g. force build, abort build, request, etc.)
- Convert all of the methods that were added for security (e.g change password, read audit events, etc.)
- Convert the remaining read-only methods (e.g. that retrieve project and status information)
- Convert the configuration modification methods (e.g. add project, delete project, etc.)
- Build the common communications library (which will need planning as well)
This breaks things down into manageable chunks. Now I probably won’t post on all of these chunks – as most of it will just be grunt work. Instead I’ll just post on some of the notable points and the lessons I learn along the way.
Step 1: Some New Classes
The very first step, before I even modify the methods, is to add some request and response classes. Since these are very generic methods, I’ve added the ServerRequest, ProjectRequest and Response classes from my plan. These classes are also very generic.
A ServerRequest contains the basic details for a request to a server. Every method needs to have at least this as the base message. This contains a timestamp, an identifier, name of the target server and the session token.
The first two properties will be used to prevent replay attacks. This is where a message is intercepted and sent to the server multiple times. While this is unlikely to bring down CC.Net (due to the way it queues requests) it could force the server to work harder than it needs to (i.e. constantly processing force build requests). These two properties limit replay attacks. I only say limit at the moment, since the messages are all sent plain text. Therefore a smart hacker could intercept a message and change these two properties for each replay! I’ll do some more on preventing this scenario later on.
The name of the target server is literally that – which server is intended to process this message. This is mainly for HTTP scenarios where multiple CC.Net servers are behind an HTTP facade.
Finally the session token is common to the security changes. The main thing to note here is I want every message to include the session token. This is a first step towards being able to completely lock down a server (e.g. even including hiding projects!)
The one property missing from ServerRequest that is often needed is the project name. This is because the project name is not required for every single request. To allow for those requests that do need a project there is the ProjectRequest class. This inherits from ServerRequest and adds the project name property.
Both ServerRequest and ProjectRequest are input classes – they send data into the methods. The corresponding output class is Response. This is a basic reply class that lets the client know what happened. As such it has a timestamp and identifier of the request, plus a result code and any error messages.
The result code can be success, failure or warning. Success and failure are fairly straight-forward, the operation either ran or it didn’t. Warning means the operation still ran, but some error messages were generated. As such the error messages need to be used together with the result code.
That describes the basic message classes, time to do something with them.
Step 2: The Contract
In WCF terms the contract is the agreement between the client and the server. It tells each party which operations can be performed and what the input and output messages are. While we are not working on a WCF application we still have the same principal in CC.Net.
Instead of a contract, CC.Net uses the ICruiseManager interface as its agreement. Now, in theory once an interface has been designed and deployed it should never be changed. Changing this interface forces all other software that is built using it to need a rebuild! But since ICruiseManager is mainly an internal interface (i.e. we haven’t documented the API for external use) I’d already modified it to allow security.
I had a chat with some of the other devs about some different approaches to adding and we came down to two:
- Modify ICruiseManager (and force everyone to rebuild)
- Add a new interface (and then figure out how to expose it)
In the end we decided on the first approach, mainly because it is simpler and I have already modified it to allow security!
So to use the new messaging classes, I removed all the overrides for Stop() and Start() and replaced each with one method that takes in a ProjectRequest and returns a Response. I also made a similar change to ICruiseServer, since the cruise managers just act as pass-through layers to this interface.
Of course, attempting to compiling the code at this point breaks lots of things!
Step 3: Changing the Server
Now to start fixing everything. Since the server is the heart of CruiseControl.Net, I’ve started there. Since most requests have a similar pattern I’ve made a helper method called ValidateRequest() in CruiseServer. This validates the timestamp and identifier (I’ve also added an identifier cache to store previous identifiers).
Each project request uses the following code:
Response response = new Response(request);try{// Validate the request and check the security tokenValidateRequest(request);if (permission.HasValue){CheckSecurity(request.SessionToken,request.ProjectName,permission.Value,eventType.Value);}// Perform the actual actionaction(request);}catch (Exception error){response.Result = ReponseResult.Failure;response.ErrorMessages.Add(error.Message);}return response;So I extracted this into a separate helper method called RunProjectRequest(). The action() call in the above code is to a delegate (Action<ProjectRequest>), and since C# allows anonymous delegates I can make calls to this method like this:
public Response Start(ProjectRequest request){Response response = RunProjectRequest(request,SecurityPermission.StartProject,SecurityEvent.StartProject,delegate(ProjectRequest arg){// Perform the actual project startif (!FireProjectStarting(arg.ProjectName)){integrationQueueManager.Start(arg.ProjectName);FireProjectStarted(arg.ProjectName);}});return response;}At the moment I only have the start and stop project methods, but this sets the background for any project-based call.
This is the main change on the server-side (other than modifying the pass-through layers). Now, the next change is to modify the clients to work with the new signatures.
Step 4: Changing the Clients
In CCTray the class that handles .Net Remoting calls for a project is RemotingCruiseProjectManager. This just calls the various methods on ICruiseManager, so I added a helper method called GenerateProjectRequest(). This takes in a session token, adds the project name and generates a complete ProjectRequest.
I also added a second method called ValidateResponse(). This checks the result on the response – if it is failed then it throws an exception (the error messages are joined together and included in the message). This is required so the methods work in the same way as previously.
Then the only change is to modify the StartProject() and StopProject() methods to use these methods. Now they work with the new messaging syntax and still appear to be the same.
I also made similar changes to ServerAggregatingCruiseManagerWrapper for the dashboard. I say similar because the parameters are slightly different (ServerAggregatingCruiseManagerWrapper uses IProjectSpecifier instead of storing project names.) However the method names are the same and the end result it the same.
So that completes the proof-of-concept for starting and stopping projects. A quick test shows that these methods still work, both with and without security.
Where to Next?
Next I need to continue modifying the rest of the methods in ICruiseManager. This may take a while, but hopefully the helper methods will simplify things.
As I previously stated, I listed the chunks that I’m going to work in, but I’m not necessarily going to post on all of them. Hopefully my next post will be to say the conversion is complete, now it’s time to look at the Common Communications Library
![]()
Stay tuned…
Stylish Passwords on the Web
A Quick Recap
In my last post (read it here) I wrote about adding password management to the server-side components. This built on the new external file security manager and allows the changing and resetting of passwords. What I hadn’t added was the ability for a client application to change the password.
In this post I’ll start with the web dashboard and add first a password login view, then a change password view and finally I’ll add the ability for an administrator to reset the password. Along the way I’ll be modifying how jQuery is used in the application and add some nice styles.
Who Are You? Really?
One of the short-comings in security via the web is the inability to enter a password. At the moment the only security mechanism in place is simple user (i.e. user name only). As almost every secure scenario requires a password, this really limits things.
To get around this I have expanded the login mechanism to also accept a password. The plug-in for user name login is UserNameSecurityPlugin, with UserNameSecurityAction as its action. I have extended the view to also display a password input box. When the user clicks on login the password is added to the credentials and sent to the server.
Since this is a simple change, I have also added a reflector-backed property to the plug-in to allow turning the password display off. This allows current configurations to continue to function as they do now. But by default the password input will now be displayed. The following shows how this page now looks:
As a final note on this, the password is not required. This is so people with just a user name can still login – they just leave the password field empty. It is completely up to the server whether a password is required or not.
At the same time I also fixed a bug in the login/logout routines. This bug meant the login/logout text was incorrect at the top of the page. In the routine that set this text, it checks the session retriever – which is responsible for retrieving the session token. Unfortunately, this value was incorrect as the login/logout routines set a different value.
To get around this I changed to using the session storer. This accepts the new value of the session token and uses it to generate the URLs. With this minor change the correct text is now displayed.
Changing Passwords
In order to change passwords, the user needs some way to get to the page. Since most of the security functions (e.g. login/logout) are at the top of the page I modified this area to also include a change password option:
Clicking on this button takes the user through to the change password page:
There is nothing special about this page. The action behind it is ChangePasswordSecurityAction – which is currently hard-coded into the system. This basically means whenever the user is logged in they will see the change password option.
The logic behind the page is also simple – check that the new passwords match and if so, send the details to the server. Once the server has finished, take whatever results come back and display them to the user. If there are any problems, display an error message.
As a final note on this, the dashboard doesn’t care what the server allows or disallows. If the security manager at the server doesn’t allow password changing the user will just get an error:
Stylish Results
In my screenshots the results of any operations are displayed in a nicely styled box. Now, since I’m not a designer (or artistic at all) I cannot take credit for these. Instead these are the work of people on the jQuery project (http://jquery.com/).
jQuery is a JavaScript library that designed to be lightweight, fast and (most importantly) cross-browser. Additionally, there are now a number of UI “widgets” available, which add functionality like tabs, dialog boxes and calendars to a plain old HTML page.
As well as this work, there is also work on-going for themes – which are basically pre-built combinations of colours and fonts to make things look good. Take a look at http://ui.jquery.com/themeroller sometime – this has some nice preset themes, plus the ability to generate your own variations!
To try and make it easier for people to change and modify the themes, I’ve added the jQuery libraries to site template. I’ve also modified the configuration so people can set their own jQuery themes. Just open dashboard.config and go down to the stylesheets section:
<stylesheets><stylesheet location="css/Black Tie/ui.all.css" name="Black Tie"/><stylesheet location="css/Blitzer/ui.all.css" name="Blitzer"/><stylesheet location="css/Excite Bike/ui.all.css" name="Excite Bike"/><stylesheet location="css/Humanity/ui.all.css" name="Humanity"/><stylesheet location="css/Start/ui.all.css" name="Start"/></stylesheets>As you can see, it is possible to set multiple style-sheets, so nice modern browsers like FireFox can offer the users different styles.
To achieve this I had to modify SiteTemplateActionDecorator, but it has the plus affect of making the jQuery functionality available to the whole site! So hopefully over time we’ll start to see some more jQuery-based interaction and styling across the whole dashboard
![]()
Resetting Passwords
As well as changing passwords, there is also the scenario when a user forgets their password (shock, horror!) In the internal security manager, this is not a problem – the administrator just retrieves the config file and looks up their password.
But, with the changes I’ve slowly been implementing, this may not be possible. Plus there is the possibility the administrator is somewhere where there is no access to the config (trying to view it in the dashboard hides the passwords). So it’s time to allow resetting passwords in the dashboard.
Currently the administrator has the ability to view all the users and diagnose their permissions. So rather than add a whole new user section I’ve just expanded on this:
This involved adding a new button to the view that allows the administrator to reset the password. At the moment this password needs to be configured in dashboard.config (it defaults to password). Clicking on the button will change the user’s password to the configured password. Of course, at this time it’s normally a good idea for the user to login and change their password!
Just like the user changing their own password, this requires a security manager that allows password changing:
Behind the scenes, this action just sends the reset password request to the server. The server validates that the user has the valid permission and then changes the password.
Dashboard Updated
These changes were pretty simple really – most of the time invested was in working on the UI side of things. I’d already made the server-side changes previously, so I just needed to make things look nice
![]()
That just leaves CCTray. After some investigation I was unable to find a good area to add this into
The current security has been hacked in, and I think adding in password management would just confuse things further! Therefore, for the time being I'm not to add password changing into CCTray.
Password Management
Starting Down the Dark Path of Password Obfuscation
One of the other devs saw my post on an external file security manager (read it here), and asked “why?” The pre-processor already handles importing files, so why would I want to duplicate the effort? The answer to this question lies not in the current capacities of the security managers, but instead further down the path of possibilities!
One of the current issues with security is all the configuration is plain text – if someone can crack the server they have access to all the passwords! Secondly, in order for passwords to be used, somebody needs to add them to the configuration – which means this person (or people) will know all the passwords. Finally, there is no way for an individual to change his or her password – they are stuck with the password they have been given
So, it’s time to start looking at password management.
Step 1: Changing Passwords
The first step to reversing these problems lies with the ability to change passwords.
With internal security, everything is defined “internally” in the configuration. While it is possible to modify this file, there can be unexpected results. The main one that I know of is the file watching – change the configuration file and the entire file is reloaded!
The external file security manager gets around this problem by putting the users and permissions in external files. If one of these files changes, the configuration watcher doesn’t know, and therefore doesn’t care.
But, before I can actually get to the point of updating these files, I first need to add some password management methods. To start with I’m going to add two new methods – ChangePassword() and ResetPassword().
ChangePassword() will change the password of a logged on user. It will use the session token to identify the user.
ResetPassword() will allow the current user to change anybody’s password. Therefore this method will need to be secured (I’m going to use the ViewSecurity permission for now).
These two methods have been added to ICruiseManager, ICruiseServer and ISecurityManager, plus all their implementations. For the manager and server implementations, they merely function as pass-through layers – the end point is the methods in the security manager implementations. For now, all of these will throw a NotImplementedException – and for the internal and null security managers this won’t change.
In due time I’ll update the clients to call these methods, but for now I’m calling them via unit tests.
All the security manager now needs to do is find the user (via the session token for changing passwords, via the user name for resetting), call the relevant method and then save the changes.
Step 2: Updating the Files
Now, since the external file security manager uses external files for its storage, it’s not too hard to update them.
The first challenge is knowing which file to update. Since potentially the password could have come from one of many files, I’ll need someway of linking the setting to the file. To do this I added a new dictionary that has the identifier as the key and the filename as the value. Now when an item is updated I can use this dictionary to find the file to update.
Now that I know the source file, the next step is to find the security item (I do by iterating through the file) and updating it. The actual update is done by calling NetReflector to generate the new XML, which is then used to replace the old XML. Finally the file is saved with the changes.
I used this approach instead of a simple find-and-replace because I cannot guarantee that the setting stores the password in an element called “password”. This approach allows the password to be stored in any applicable element (or even externally again).
As a side note, another idea I want to implement is encrypting passwords. This will remove the responsibility for passwords from the admin and make them a little bit more secure. This is another reason why I didn’t want to directly update the XML.
However, one down side to this approach is the XML is slightly changed. NetReflector always uses elements and it strips comments – but since this allows more flexibility long-term I can live with these changes.
Step 3: The Clients
The final step for getting this to work is to allow the actual users to update their own passwords. However, since this is not easy (for a number of reasons) I’m going to leave this for my next post (or posts).
For now, know that I have updated ICruiseManager to allow password changes over .Net Remoting. Since .Net Remoting is the only way to connect to the actual server, this opens the way for clients to change passwords. In terms of what is required, the following breaks down each client:
- For the web dashboard I need to add a login handler for password users, and then add the change password view. I also need to add an XML version for HTTP clients to use.
- For CCTray I need to figure out where to add this functionality, and then modify both the transport mechanisms and add the dialog.
- Finally, I need to work out where to allow resetting passwords (currently I’m thinking this will be a web dashboard task).
So, stay tuned for allowing clients to change their passwords
Security Scenario 3: Small Open Source
The Saga Continues…
Previously I’ve put together two scenarios on security. These were:
Both of these scenarios covered in-house development projects. In this scenario I’m going to change tack and look at a small open source project.
Petros has been working on an open source project called Midas for about a year now. This project is hosted on SourceForge, and he is the only administrator. However recently it’s been getting some press and other people have volunteered to help, which he thinks is wonderful!
He has convinced his work (a chemical conversion company) to host a CruiseControl.Net instance for the community to use. However, he doesn’t want to allow everybody full access to the server. He is happy to let everybody view the current status, but he only wants to let approved developers force build. He doesn’t want to let anybody stop or start the projects. Finally he wants to know who performs any release builds.
The Build Setup
Petros’s build setup is really simple – there is only two projects in his config.
The first project is a CI build – it polls every five minutes and performs a build if any changes are detected. This does the standard compile, run unit tests and generate installer. The outputs are stored in a publically accessible location.
The second project is a releasing build. Again it does the compile, unit tests, etc, except this time in release mode. The outputs are then stored on his open source website and a release post is added to tell the world. This project must be manually forced.
The Goals
Since Petros has full access to the server and is the only person who needs access, there will be only one security zone.
This security zone will grant approved developers access to force or abort a build on the project. Currently security does not prevent viewing the reports, so this will not require configuration.
To keep things simple, all force builds will be audited.
The Plan
There will be only one security group – CanBuild. This group will have force build access to both projects, but no other access.
All developers who need force build rights will be manually added to this group.
The Configuration
The full configuration is as follows:
<cruisecontrol><project name="Open_Source(Build)"><workingDirectory>C:\Build\OSB</workingDirectory><artifactDirectory>C:\Build\OSB\Deploy</artifactDirectory><webURL>http://localhost/server/local/project/LAS-Main/ViewProjectReport.aspx</webURL><triggers><intervalTrigger buildCondition="IfModificationExists" seconds="300"/></triggers><publishers><xmllogger/></publishers></project><project name="Open_Source(Deploy)"><workingDirectory>C:\Build\OSD</workingDirectory><artifactDirectory>C:\Build\OSD\Deploy</artifactDirectory><webURL>http://localhost/server/local/project/LAS-Main/ViewProjectReport.aspx</webURL><triggers><intervalTrigger buildCondition="IfModificationExists" seconds="300"/></triggers><publishers><xmllogger/></publishers></project></cruisecontrol>Again the first step is turning on security – just added the internalSecurity element, plus the users and permissions children.
Adding the Roles and Users
Now the security has been added the next step is to add the users and the CanBuild security group. Since this is an open source project, Petros doesn’t want to bother with much security. Therefore each person who is allowed to build has been added as a password user. At the moment, this is only Petros himself:
<users><passwordUser name="petros" password="whoareyou"/></users>The role is very similar, but just contains the force build permission and the list of users:
<permissions><rolePermission name="all" defaultRight="Deny" forceBuild="Allow"><users><userName name="petros"/></users></rolePermission></permissions>I’ve also set the default right on the internal security element to Deny, so everyone else will be unable to do a force build.
Locking Down the Projects
The final step for preventing force builds is to secure the projects. This is the same configuration as previous:
<security type="defaultProjectSecurity"><permissions><rolePermission name="all" ref="all"/></permissions></security>Again, this checks that the user is in the role, and if so the user can do a force build. All other permissions are denied at the server level.
Audit Logging
The final step once more is the auditing. Since this is very simple, I’ve just added the default audit logger block:
<audit><xmlFileAudit/></audit>I haven’t added an audit reader as there is no way of accessing it at the moment.
Some Comments
Again, there’s still no way of allowing password users to log onto the dashboard (hence why I haven’t covered it).
One issue I did think about with this scenario is sending the login requests to a remote service (e.g. something like SourceForge or CodePlex). At the moment I don’t know whether these services even have a login API, but if so it would be nice to have a pass-through for this. However since it opens up a whole set of security issues I decided not to even try it!