How to override the new plain white background in Twitter

As many of you know, Twitter just removed the ability to customize backgrounds on the web client. It’s bad-ish that they removed the ability to add custom graphics as a back ground, but far worse, they removed the ability to change the background colour, leaving users with an eye-bleeding, headache inducing bluish white. new-9

Well, “the Man” might be wanting us to just shut up and deal with it but we don’t have to take it. StyleBot allows us to edit the CSS (stylesheet) of any page. It is originally intended as an accessibility tool as it allows users to change colours (to make them high contrast), font sizes  etc, however we can use is to stick it to the Twitter man, The great thing about this is that we can save the styles and the plugin changes the colours pretty much permanently.

The following works for Chrome and there’s probably similar things you can do in Firefox & maybe IE.

Install the free StyleBot from the Chrome Store

You will now have a CSS button in the upper right. Right click on it and select Options.


Select the Styles tab


Click Add a New Style

Then add the URL and the 3 lines of CSS code. (Important: everything is Case Sensitive)

body {
    background-color: #4a7383;

I like a dark-ish blue but you can pick what ever value you want. There’s a good colour picker tool here at Mozilla


Now click the save buttton, reload your Twitter tab and say ahhhh.


You’ve got a background that you control. And there’s nothing the Man can do about it. You can go crazy and change fonts, layouts and a lot more or use one of the pre-built style for Twitter & many other sites from StyleBot Social but for me, I just wanted the background colour changed.

Note: this only affects what -you- see. If you wanted to customize your Twitter page for others, you’re out of luck. At least until Twitter starts charging you to customize it. Which is my guess as to why they have done this miserable thing in the first place.

Update: Noticed that Twitter cards are changed by this as well.


I don’t like that but it’s easy to change. All we need is to do is add an entry for Twitter Cards.

body {
   background-color: #4a7383;
div.TwitterCardsGrid {
    background-color: #ffffff;

Another little tweak.

if you use and don’t want that page altered (it’s quite hard to read with a dark background.)

Add an entry with just a white (#ffffff) background.


Then modify the original URL to ^


This applies the fix to the main twitter page but not to analytics.

Update #3

Firefox users can install Stylish


Click the S icon and then Write new style > for



Place your cursor between the { } on line 4 and paste the CSS code from above. Give it a name and click save.



Coldfusion Directory Monitoring with Event Gateways

This just came up on Twitter today and I realized I hadn’t posted about it so here you are.

Many hacks of ColdFusion over the years have been through people manipulating the CFIDE directory. (the most famous being the FCKeditor Hack) There are many ways to combat this but here’s a simple one.

Using the DirectoryWatcher Event Gateway built into CF versions 8 & up, you can in just a few minutes set up some code to monitor & alert you of changes to the CFIDE (or any other) directory.

You need 2 piece of code.

The Config File


# The directory you want to watch. If you are entering a Windows path
# either use forward slashes (C:/mydir) or escape the back slashes (C:\\mydir). 
# Should we watch the directory and all subdirectories too
# Default is no. Set to 'yes' to do the recursion. 
# The interval between checks, in milliseconds
# Default is 10 seconds
# The comma separated list of extensions to match.
# Default is * - all files
# CFC Function for file Change events
# Default is onChange, set to nothing if you don't want to see these events
# CFC Function for file Add events
# Default is onAdd, set to nothing if you don't want to see these events
# CFC Function for file Delete events
# Default is onDelete, set to nothing if you don't want to see these events

and a CFC to use for the Gateway. (note: I don’t have Delete events turned on as I delete CFIDE/Admin files when not in use…for another post)


<cffunction name="onAdd" returntype="any">
<cfargument name="CFEvent" type="struct" required="yes">
<cfset data =>

 <cfmail to="" 
<cfdump var="#data#">

<cffunction name="onCHANGE" returntype="any">
<cfargument name="CFEvent" type="struct" required="yes">
<cfset data =>

 <cfmail to="" 
<cfdump var="#data#">


Then all you need to do is create the Event Gateway


Now, if any files are changed or there are files added, you are sent an email. You could also use Event Gateways to send a an SMS message so you can be notified anywhere, anytime.


Security Basics: Email Survey Requests are Dangerous

Quick post this afternoon based on a semi regular security email I send out to all users at work.

Both at home and at work, we often see companies sending out email surveys, often with enticements like coupons, entries in draws etc.

It is often extremely hard to tell if these are legitimate.

Here are 2 I’ve recently received



Now we know that the very first thing to do when we are trying to find out if an email is from a legitimate source is check the FROM address.

(Of course we always keep in mind that FROM addresses can be spoofed, right?)

Here are the 2 FROM addresses for the above emails



Can you tell which is legitimate? Knowing that emails from Walmart or Telus should have a @ or @ (same origin emails) doesn’t help in the case of many legitimate surveys. As stated in the Telus email, companies often contract survey work out to companies who specialize in this kind of thing.

Neither of the above is from same origin but there are some clues in the emails themselves that hint which is legitimate and which is not.


  • One says you’ve WON!* something, the other says you’ll be entered for a chance to win. Guess which is more likely to be legitimate.
  • One only offers links to click, the other offers a copy and paste option. Copy and paste allows you to see where the link actually goes. (of course we all hover over links to see where they point always in any case right?)
  • We actually -are- a client of Telus. We/I don’t have any Walmart association (ask yourself “how did they get my email”)


So yes, the Telus email is legitimate. The Walmart one points to a malware exploit.

My advice is to ignore and delete survey requests from all senders.

*See Rule Of Exclamation Points In Email


Rule Of Exclamation Points In Email

The chances of a email being legitimate is inversely proportional to the number of exclamation marks it contains!!!



http:BL_CFML – A Project Honeypot Blacklist Implementation for ColdFusion.

At the urging of Charlie Arehart, I’ve cleaned up my http:BL_CFML implementation and added a bit more in the way of comments. I’m putting this here so it’s easy to link to from the http:BL implementations site. For more in depth information about the code, please check out the links to my previous posts listed below.

Stopping Comment Spammers & Email Harvesters with Coldfusion & Project Honeypot

Project Honeypot & Coldfusion Part 2

<cfcomponent displayname="http:BL_CFML v1.01">

<!--- Some original code from (which no longer exists) with modifications by JayB
More detailed explanation available here:

You are free to use or modify this code for any purpose provided links to original posts are maintained.
This CFC contains the following functions

honeypotCheck: Main framework
newVisitorCheck:  Check with our white list table to see if this ip has visited in the last 3 months.
addToWhiteList: IP's that check as clean against http:BL are added to our white list table to increase page load performance.
getHostAddress: does a reverse lookup of the client IP address against the blacklist at http:BL.
reverseIP: return IP in reverse format as required by http:BL api

You need to create a new table to track white listed IP's

CREATE TABLE `visitor_ip_addys` (
`ip_id` int(10) unsigned NOT NULL auto_increment,
`ipaddy` varchar(15) NOT NULL,
`visitdate` datetime NOT NULL,
PRIMARY KEY  (`ip_id`)

CREATE TABLE [dbo].[visitor_ip_addys](
[ip_id] [int] IDENTITY(1,1) NOT NULL,
[ipaddy] [varchar](15) NOT NULL,
[visitdate] [datetime] NOT NULL,
[ip_id] ASC

The table is cleared daily of IP's older than 3 months to revalidate visitors in case they may have been
compromised in the time and to keep table size reasonable

<cffunction name="honeypotCheck" returntype="struct" hint="Check Project HoneyPot http:BL">
<cfargument name="ip" required="yes" type="string">
<cfset var aVal = "">
<!---  Make sure to replace {your_API_key} with the API key you receive from the http:BL project --->
<cfset var hpkey = "{your_API_key}">
<cfset var stRet = structNew()>
<cfset var result="">
<cfinvoke method="newVisitorCheck" returnvariable="result">
<cfinvokeargument name="ip" value="#arguments.ip#" />
<cfif result eq "new">
<!--- do the blacklist lookup IP values --->
<cfset aVal = listToArray(gethostaddress("#hpkey#.#reverseip(arguments.ip)"),".")>
<cfif aVal[1] eq "IP-Address not known">
<!--- then no match on the Black list --->
<!--- set a value indicating ok address --->
<cfset stRet = {type=99}>
<!--- insert into visitor_ip_addys table --->
<cfinvoke method="addToWhiteList" returnvariable="result">
<cfinvokeargument name="ip" value="#arguments.ip#" />
<!--- there was a match so set the return values --->
<cfset stRet.days = aVal[2]>
<cfset stRet.threat = aVal[3]>
<cfset stRet.type = aVal[4]>
<!--- Get the HP info message ie: threat level --->
<cfswitch expression="#aVal[4]#">
<cfcase value="0">
<cfset stRet.message = "Search Engine (0)">
<cfcase value="1">
<cfset stRet.message = "Suspicious (1)">
<cfcase value="2">
<cfset stRet.message = "Harvester (2)">
<cfcase value="3">
<cfset stRet.message = "Suspicious & Harvester (1+2)">
<cfcase value="4">
<cfset stRet.message = "Comment Spammer (4)">
<cfcase value="5">
<cfset stRet.message = "Suspicious & Comment Spammer (1+4)">
<cfcase value="6">
<cfset stRet.message = "Harvester & Comment Spammer (2+4)">
<cfcase value="7">
<cfset stRet.message = "Suspicious & Harvester & Comment Spammer (1+2+4)">
<!--- good address  --->
<cfset stRet = {type=99}>
<cfreturn stRet>

<cffunction name="newVisitorCheck" returntype="string">
<cfargument name="ip" required="yes" type="string">
<cfset var vQry = "">
<cfset var result="">
<cfquery name="vQry" datasource="#local.dsn#">
select ipaddy from visitor_ip_addys where ipaddy = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.ip#">
<cfif vQry.recordcount eq 0>
<!--- then it's a new visitor  --->
<cfset result = "new">
<cfset result ="existing">
<cfreturn result>

<cffunction name="addToWhiteList" hint="I add client IP's that don't match with http:BL entries (ie: not comment spammer)">
<cfargument name="ip" type="string" required="true">
<cfset var iQry = "">
<cfquery name="iQry" datasource="#local.dsn#">
insert into visitor_ip_addys (ipaddy, visitdate) values
(<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.ip#">,
<cfqueryparam cfsqltype="cf_sql_timestamp" value="#now()#"> )

<cffunction name="ipTableCleanup" hint="I run from a daily scheduled task">
<cfset var deleteIP = "">
<cfquery name="deleteIP" datasource="#local.dsn#">
delete from visitor_ip_addys where visitdate <= DATE_ADD(CURRENT_TIMESTAMP, INTERVAL -90 day)

<cffunction name="getHostAddress" returntype="string" hint="I do the dns lookup against the http:bl servers">
<cfargument name="host" required="Yes" type="string">
<cfset var obj = "">
<cfset var result="">
<!--- error handling as error is thrown if host lookup has no match in http:BL ie: it's not been reported as a problem --->
<!--- Init class --->
<cfset obj = CreateObject("java", "")>
<cfset result =  obj.getByName(host).getHostAddress() >
<cfcatch type="any">
<!--- an "error" in this case is an unknown address, which means it is not reported to http:BL --->
<cfset result="IP-Address not known">
<!--- Return result --->
<cfreturn result>

<cffunction name="reverseIP" returntype="string" hint=" I return IP in reverse format as required by http:BL api">
<cfargument name="ip" required="Yes" type="string">
<cfset var aIp = listToArray(arguments.ip,".")>
<!--- Return IP reversed --->
<cfreturn aIp[4] & "." & aIp[3] & "." & aIp[2] & "." & aIp[1]>


To use this CFC, simply add the following code to a page on your site where a comment spammer or email harvester would visit. If they are on the Blacklist, they will get a 404 error when they try to visit that page.

You can also add this on a site wide include if you like (but this could have performance implications)

<!--- Check Project HoneyPot --->
<cfinvoke method="honeypotcheck" component="com.HoneyPotdns" returnvariable="stCheck">
<cfinvokeargument name="ip" value="#cgi.remote_addr#" />

<cfif isDefined("stCheck") AND (stCheck.type GTE 4 AND stCheck.type LTE 7)>
<!--- Send a simple 404 message rather than a hey spammer get lost message. No use in poking a hornets nest --->
<cfheader statuscode="404" statustext="Not Found">
404 Not Found

How CASL could be a SpearPhisher’s Delight

For those readers in Canada, you may or may not know about CASL (Canada’s Anti-Spam Legislation – text in italic below are quotes from this site). CASL is a potential nightmare for business and non-profit orgs. CASL in short, applies to “Anyone who makes use of commercial electronic messages, is involved with the alteration of transmission data, or produces or installs computer programs.” If you run a business and turn a computer on in the morning, it will probably affect you. And it’s going to affect you soon. CASL goes into effect July 1, 2014.

I’m not going to go into the details of CASL (just read up in the link above), but the basics are as follows..

  • CASL applies to any CEM (commercial electronic message) which can be email, text, SMS, social media (Facebook, Twitter, LinkedIn etc)
  • To send a CEM to anyone in Canada after July 1 you must have their consent. Consent must be Opt-In
  • You must provide a physical address with each CEM
  • You must provide a simple and quick method to unsubscribe (remove consent)
  • Consent records must be kept specifying:
    • whether consent was obtained in writing or orally,
    • when it was obtained,
    • why it was obtained, and
    • the manner in which it was obtained.

There are also provisions affecting those who write programs and apps, as well as those who provide 3rd party management of end user data such as ad agencies, newsletter services (MailChimp etc).

Oh, and the penalties for non-compliance are up to $1,000,000 for individuals and $10,000,000 for corporations and non-profits, with director liability thrown in for fun. And pending private damages rules coming into force in 2017. So ya. Fun, fun, fun.

Now what does CASL have to do with Spearphishing (or phishing in general). It’s that consent thing. Companies are starting to send out email like the one below.


Expect your in-boxes to start filling up with these during the month of June because companies are just now figuring out that after July 1, you can’t get consent to send emails….by sending an email, because it would violate CASL. Nice.


Now as it happens, this is a perfectly legitimate email from a trade partner but does anyone see a problem with it?

It’s EXACTLY the kind of email we get from a phisher. A single button or link saying click me please. It’s a huge problem for those of us who have telling our users for years not to click links in email. Now your accounting, purchasing and sales departments are going to be receiving dozens of these and while most of them will be legitimate, not all of them will be. It’s guaranteed that there are phishers out there crafting convincing looking Opt-In consent email like the one above.

So what to do.

I’m sending out a policy update today to reiterate to users that they should not be clicking links in email. If they want to consent to further CEM communications with the sender, I’m having them visit the website directly (by typing in the URL).

I’m also knee deep in preparing our systems for July 1 which also includes setting up my own opt-in email will encourage users to visit our website. I suppose I’ll even put in a CONSENT button, because that will get greater opt-in than asking recipients to go to a website. What a nightmare. Thanks GoC.

LRSWIW: A password scheme you can live with and is (probably) pretty secure

If you follow tech trends at all, you know passwords are under threat of becoming useless. Most commonly used passwords have been published on the internet to be used as “rainbow tables” and “dictionaries” for “brute force attacks”. These are basically long lists (some in the 10’s of millions) of passwords stolen from various websites around the world. Without being too technical, they are used to match passwords stolen from a newly compromised website against known password lists. We’re told use long passwords with random characters and never re-use a password on different websites. I don’t know about you but I probably have accounts set up at 50 or more websites. Long, random and unique passwords of 50+ sites? I’m getting too old for that. There are software “password vaults” that can help with this but I think I have come up with a pretty good scheme for decent security, that won’t be too difficult to use. Before I get to that a little background to help you understand what I’m proposing.

Cracking passwords is not what you think

For the most part, when people think about cracking passwords, they think of some hacker furiously typing some code to try and get access to a system while the clock ticks down to zero. (for an hilarious example click here and start typing random text In reality, most password cracking is done “offline”. This means the bad guy gets access to a website or other corporate database, saves it to their system and runs password cracking programs at their leisure. No ticking clock, at least not if they’ve covered their tracks while getting the new database (or if they are in another country).

Getting a little technical, it’s important to know how passwords  are stored when you sign up with a website. You can skip to the bottom at any time if you just want to see the password scheme but I encourage you to read this to understand why it’s important.

Stored as plain text.

Very simply what you enter into the password field is saved to the database as text. When you go to log in next time, the website simply compares the text you enter with the text stored in the database. However, even if you enter the World’s Most Secure Password ™ , it will be there for all to see if the database is stolen. This is very bad but some websites still do it and you won’t ever know until your bank calls you.

 Stored as a One Way Hash

Hashing a password is a common way of securing passwords in a database. When you submit a password on a website, the software takes that text and turns it into a string of numbers and letters.

For example “password” becomes 5F4DCC3B5AA765D61D8327DEB882CF99

This string is then saved to the database. The thing about one way hashes at they can’t be “unencrypted”.  How then does the system know that when you enter “password” to log in next time, you really mean “5F4DCC3B5AA765D61D8327DEB882CF99”. It’s simple. While one way hashes are un-decryptable, they are always the same for as specific input text. The one way hash for “password” will -always- be “5F4DCC3B5AA765D61D8327DEB882CF99” The login software takes the text you input, hashes it again, and compares the new hashed value against the one stored in the database and lets you in if it matches.

input “password” + hash = “5F4DCC3B5AA765D61D8327DEB882CF99”  if  database hash = “5F4DCC3B5AA765D61D8327DEB882CF99” then allow login

The problem with simple one way hashes is exactly this property. No matter what website you log into the hash for “password” will always be the same. So will the hash value of the World’s Most Secure Password ™ (which is 1C84018561A5838CB47C776E07B0B0E0 in case you were wondering). What crackers have done is taken dictionaries, word lists and most damaging, stolen plain text password databases and created a giant hash list

  • password = 5F4DCC3B5AA765D61D8327DEB882CF99
  • qwerty = D8578EDF8458CE06FBC5BB76A58C5CA4
  • LetMeIn = BC9D9CB353C87531F61D6F21D5CC072E
  • World’s Most Secure Password ™ = 1C84018561A5838CB47C776E07B0B0E0

Now when they get a new hashed database they simply run a comparison against these known hashes.

email                                     password       D8578EDF8458CE06FBC5BB76A58C5CA4

Guess what? We now know Buddy’s password. Did Buddy use the same one for his bank? Oops!

Now maybe Buddy is smarter than using “qwerty” as a password. He’s been told to use a mix of letters, numbers and special characters. He also know that if your password is less than 8 characters, it will take a modern password cracking system less than 5 minutes to discover it – no matter how random it is.  So his password is:

Dadof3g8kids = A4334A757F1438814BD6B8AB89DE1B5E

While this looks pretty secure, there are 2 problems. First, if anyone else has used this password (and it’s actually a pretty common combination) on a compromised plain text database, then he’s immediately lost. The second is a little bit more technically involved and a few years ago wouldn’t have been feasible. However, with the immense computing power available now, these types of passwords are no longer secure. Basically you dump the password into a very fast computer (or clusters of computers like Amazon Web Services) and try every possible combination of numbers and letters. Because “dad”, “of” and “kids”  are very common words, they exist in dictionary tables and these are tried first, making the time it takes to discover the hash much shorter. New systems can check -billions- of hashes per second.

Stored as a One Way Hash with Salt

“Salting” a hash improves security because it adds a degree of randomness to the hash. When the hash is first created an additional hash sequence of random text is added.

password = “password” = 5F4DCC3B5AA765D61D8327DEB882CF99

salt = “some text” = 552E21CD4CD9918678E3C1A0DF491BC3

hash of salt + hash = C53F71D82F81AED989D4FCA89E2959B4

So now instead of storing password 5F4DCC3B5AA765D61D8327DEB882CF99 we store C53F71D82F81AED989D4FCA89E2959B4 which helps defeat the simple comparison lookup we saw earlier. However crackers are smart and they have several techniques to figure out what the Salt is. This is pretty simple if the website uses the same salt for every hashed password. It’s also possible (although more difficult) if the salt is short or not random.

For my websites I use Hash(GenerateSecretKey(“AES”), “SHA-512”) This generates a salt that looks like


When we combine the Hashed password with the hashed Salt and hash the result. we get a resulting hash that is reasonably secure.

Long, Random and Unique – How do you manage it?

Ok, so here is my scheme. It’s simply not feasible to remember 50+ long, random & unique passwords and yet as we have seen, it’s so important. What I’ve been doing for a while now is what I call the

LRSWIW (Long Random String with website identification word)

Make up a random string of characters like


Memorize it! At 12 random characters it will already be difficult to crack (at least on sites using proper hashing techniques). If you want to be more secure create a longer one.

Then when you create a password on a website, use that string and add some type of reminder text (WIW) to the password that is specific to that website and do that for every website. IF the site allows you, separate the string from the WIW with a space as this makes things even more secure.

A7b546!pk*Bt myBank = 7927D4FE45FE29C56DE2BDFA50870DEB

A7b546!pk*Bt myEmail = 45F2EA292F3AB21E54C6453E2BD14C82

A7b546!pk*Bt FaceBook = C07EC75E2F284E582D29A054DB8EB81D

A7b546!pk*Bt Twitter = 53F2D8ADFF4791E4B8E0D86FE31E1B85

As you can see each hash is different so if the Facebook password is compromised, Facebook’s hashed password tables are compromised none of the others will be (probably).

What we’ve created is a long, random string with special characters for every website we sign in on and yet once we have memorized the random string, all we really need to remember is which WIW we used for the site. And since we are using the random string many times a day or week, it’s easy to remember after the first few days so you can really make it quite long.

I find this solution to be really simple and I believe it to be pretty strong. I’d love to hear what any crypto-geeks out there have to say.

A couple of closing notes.

  • Any website that offers to “send you your password” is NOT secure. The ONLY option should be to Reset your password. Let them know that what they are doing is wrong.
  • Any website that limits you to X number of characters or only X,Y and Z characters, or no spaces is risking your security. Let them know that what they are doing is wrong.
  • As noted above, no matter how strong your password is, it’s all for naught if the website has poor security practices. This is why unique passwords are so important at limiting the damage and preventing a single password compromise from affecting the rest of your online life.

I think we’re approaching the “end of the password” as we know it as the bad guys have more access to incredibly sophisticated technology. The LRSWIW scheme is making the best of a worsening situation but it’s better than nothing and is simple to use.

I hope you find it helpful. Let me know in the comments!

See an update to this HERE




BlackArmor Data Recovery Part 2

Note: This is a continuation of my previous post

Since I don’t really have the time to play around with installing Debian on the BA440 I’ve decided to add the missing drive back in to the RAiD5 array and use my Debian box with the drives attached in the USB docking ports as an iSCSI endpoint. Not exactly a tidy desktop solution but this will go in the server room. If I get some spare time I will look at replacing the firmware on the BA. If I do, I’ll be sure to write about it.

To add my missing drive, the one the BA “recover” process messed up , I first have to identify it.

# cat /proc/partitions

You should see your system partitions probably /dev/sd[abc…] all 3 of your /dev/sd[bcde] paritions (one of the [bcde] will be the missing drive so it won’t be listed, and then a root partition for the new drive.

The missing or new drive will show up as /dev/sdf

If it’s new all that will be there is /dev/sdf and you can skip this next step.

Lots of partition tutorials will tell you to use fdisk and/or cfdisk for partition work but these won’t work with the GPT format of the BA, we need to use parted. GPT is a newer partition format used for large disk partitions and parted is the tool built to manage them.

The aborted BA recovery process tried to create partitions that weren’t correct (partition sizes did not match the others in the array) so I had to delete the partitions.

IMPORTANT: This destroys any data on the disk so make sure you’ve got the correct disk reference.

To do this run

# parted /sdf
<parted> rm 1
<parted> rm 2
<parted> rm 3
<parted> quit

rm removes the partitions. If you rerun cat /proc/partitions you will now see just /dev/sdf.

Next we need to get the exact details of the setup of the disks in the array so we run parted on a member of the array. It doesn’t matter which one as they are all the same structure.

# parted /sdd
<parted> print

Number    Start     End     Size   File system      Name       Flags
1         100MB     1169MB  1069MB ext3                        raid
2         1169MB    2239MB  1070MB linux-swap                  raid
3         2239MB    2773MB  534MB  ext3                        raid
4         2773MB    1000GB  997GB                   primary    raid
<parted> quit

Copy this output down or print it out. Important: Make sure you quit parted otherwise the next steps you make will be on the good disk in the array. You don’t want that.

Now go back and use parted on the new drive /dev/sdf

While in parted we’ll use mkpart [part-type fs-type name] start end.

For the first 3 partitions we’ll define the part-type which is either ext3 or linux-swap. For the last we’ll assign the fs-name which is “primary”. We do not define a part-type for our 4th partition which will become our data partition.

# parted /sdf
<parted> mkpart ext3 100MB 1069MB
<parted> mkpart linux-swap 1169MB 2239MB
<parted> mkpart ext3 2239MB 2773MB
<parted> mkpart primary 2773MB 1000GB
<parted> quit

If you rerun cat /proc/partitions you will see that /dev/sdf now has 4 partitions


Now all we need to do is add /dev/sdf4 to our array. To do this we go back to mdadm. From the last post we remember that /dev/md3 is our raid array.

mdadm --manage /dev/md3 --add /dev/sdf4

The  disk is now added to the array. You can check its status by running

mdadm --detail /dev/md3

Raid Level: Raid5
Raid Devices: 4
Total Devices: 4
State: clean, degraded, recovering
Active devices: 3
Working devices: 4
Failed devices: 0
Spare devices: 1
Rebuild status 3% complete
Raid Device
0 active sync /dev/sdb4
2 active sync /dev/sdd4
4 spare rebuilding sync /dev/sdf4
3 active sync /dev/sde4

I’ve edited the output to just the relevant bits. You can see the array is rebuilding. This can take a long time depending on the size of the drives. In the time it took to write this the array has only moved from 3% to 4% complete. This is normal and in 8 -10 hours or so, we’ll have a rebuilt 4 disk RAID5 array.

Once that is complete, I’ll look at setting up some new iSCSI mount points and moving it into the server room.