Ryan Detzel

Vanish.io - Secure Self-destructing Messages.

Vanish.io is pretty simple. You enter your message and hit Create Message and a new secure self-destructing message is created for you to send to whom ever you want. Once the message has been destroyed there is absolutely no way of getting it back. There are no backups, no logs, and no digital trail what so ever. The prototype was built so I could send co-workers their passwords and other important information without having to send it through email or chat which is logged and archived. I quickly found it useful to also send my wife and friends sensitive information via email like credit card information, prescription details and personal messages I’d rather not have permanently archived in the cloud. :-)

After testing this out on dozens of people and thousands of messages I’m ready to see if anyone else wants to use it. I’m also looking for tips on how to make it even more secure and better (attachments, emoticons) so if you have any ideas please leave a comment. Hopefully you find vanish.io as useful as I do.

Tip

Even though the message is not tied to you in anyway if you’re going to send certain information that doesn’t need an identity to be useful (credit card number, social security, etc) it’s best to do it in a series of messages. For example take the first eight digits of your credit card and send it in one message and take the other eight and send those in another. This just makes it a little more safe.

Template Monster

I’ve been playing a lot with backbone.js and one of the very intriguing things (which exists outside of backbone.js) is the idea of javascript templates and defining the html template on the page and then rendering it with javascript. Since I’ve been using backbone.js it’s been natural to use the underscore template system but one of the things I found when actually making an application with it is that your source quickly gets cluttered with templates that look like this:

1
2
3
<script type="text/template" id="template_on_page">
    <p>This is a template from <%=template_engine%></p>
</script>

This works for small pages but on the current app I’m working on I have over 30 templates defined. Not only does this make for a huge (okay, not really that huge) download but it’s hard to find and make changes to the templates.

One of the suggestions people have is to put them in external files which solves the second issue but seems very clunky and if I include 30+ external files they’ll all download on page load no matter what.

The solution, Template Monster. Template monster is a very small and basic script that allows you to queue up or asynchronously download your template files and use them when they’re needed (or just before). This solves both issues; the templates are no longer all stuffed on the page and we can now only download templates when we need them.

I posted it up on Github and tested it with the underscore.js template system but it should work with any template library. Feel free to send pull requests.

https://github.com/ryanrdetzel/Template-Monster

Django 1.4 Project and App Templates

I love Django and I use it for every project but the one thing I hated was having to setup the settings and file/directory structures every time I wanted to start a new project/app. Apparently I wasn’t the only one that disliked this because starting in Django 1.4 we now have project and app templates to work with.

The startapp and startproject management commands now have a –template option for specifying a path or URL to a custom app or project template.

Awesome! What this means is you can setup a project/app just the way you want it and then use that as a template for all future projects or apps you create. So if you like your settings files named foo.py you can do that or if you want your templates directory at the root instead of in each app go ahead and do that too.

I found it fasinating how people setup their projects and I have yet to find another Django developer that does it the exact same way as I do but that might changed now that we have templates. As more people start to create and open source their templates we’ll start to see a trend and some popular ones will surface just like jQuery did for javascript frameworks and bootstrap did for front-end frameworks. Here are my templates in case you’re curious:

Project Template

App Template

Some tips on using django templates

Passing a url

When you’re passing the –template option to startproject or startapp you can use a url instead of a local directory. This means that if you publish your template to a website or Github you can easily start a new project/app with one command:

django-admin.py startproject --template https://github.com/ryanrdetzel/django-base-template/zipball/master --extension py new_project_name

Note: If you’re using github take note of the changes to the path(zipball/master). This tells Github to compress your source into a zipball so it can be downloaded. You’ll have to do this if you want the template option to pick it up.

Template tags in your files

When django is using your template to create a new project/app it does so by rendering certain files through the template engine. This allows you to use tags as you would in a normal django template within your project/app and have them replaced during the process. The startproject provides the following tags.

  • project_name – the project name as passed to the command
  • project_directory – the full path of the newly created project
  • secret_key – a random key for the SECRET_KEY setting

So if in your settings.py if you want the name of your project(maybe for installed apps) you can simpley use ” and when django is creating your project it will replace this tag with the name you passed into the startproject command.

This works for apps too, the startapp commands surfaces the following tags:

  • app_name – the app name as passed to the command
  • app_directory – the full path of the newly created app

Please note this warning which I did come across when playing with this feature.

When the app template files are rendered with the Django template engine (by default all *.py files), Django will also replace all stray template variables contained. For example, if one of the Python files contains a docstring explaining a particular feature related to template rendering, it might result in an incorrect example.

To work around this problem, you can use the templatetag to “escape” the various parts of the template syntax or don’t use any variables that you don’t want replaced in your templates. ;-) I hit this when I tried to include my app template in my project template. What happend is when I created a new project the tags were replaced with nothing since the startproject command tried to render them first. Instead of escaping I felt it was best to break the app template out anyway so I would use it individually.

Hopefully this gives you enough information on templates to start researching and creating your own. Hit me up in the comments if you’ve published your template, as I said it’s always interesting to see how others setup their projects and I might borrow some ideas from your template to include in my own.

Swap Redis Servers Without Downtime

I use Redis for a lot of things ever since I first discovered it a few years ago. It’s fast, it’s easy to setup and it has some really great features. One of the projects I use it for is recording/tracking metrics on our site. Basically, as requests come into our Django app we do some analysis on it and push a snippet into a redis queue for later retrieval. I do this because it’s really really fast and I don’t have to worry about slowing down the request. Recently we needed to upgrade some servers which required me to move the redis server to another location and much to my surprise redis has a built in feature that allows for quick and easy master swap with zero downtime.

I could give step by step instructions but it’s dead simple and they do a pretty good job of explaining it on their blog. The swap took literaly five minutes and we didn’t miss one single impression during the swap over.

I love you Redis.

1,532 Days Without a Code Change. How a Pet Project That Hasn’t Been Touched in Over Four Years Still Gets Daily Signups.

The other day I got a phone call coming home from vacation. The call was from a New York number that wasn’t in my contacts which usually I’d ignore but since my brother was in NY I figured maybe it was a friend of his and something had gone wrong so I answered it. On the other end was a very faint voice, she asked cautiously, “Hi…is this the owner of Invoice Journal?”. I hesitated, in the 4+ years of running Invoice Journal I’ve never received a phone call so a million thoughts rushed through my mind before stumbling to answer “Yes”. “Okay, Umm, do you know when the site will be back up? I’ve been using this for all of my invoices and I haven’t been able to get on in days.” She uttered. “I really need to get my invoices out so if the site isn’t coming back up could you please send me the invoices I have in the system?”.

Okay, Umm, do you know when the site will be back up? I’ve been using this for all of my invoices and I haven’t been able to get on in days.

Hmm, at first I was a little taken back since I hadn’t thought about nor touched Invoice Journal in over four years. I had forgotten about it, I had neglected it, I had basically written it off but this soft voice reminded me that all though I had written it off she had not and that she actually relied on it to run her business. I was amazed. Being caught off guard and not really sure why it was in fact down I told her I was currently in the car but as soon as I got home I would check on the site and bring it back up for her. The call ended and for the next two hours Invoice Journal kept popping in my head.

Invoice Journal started as a pet project to occupy my time as I commuted on the train into work. I had roughly two hours of free time on the train everyday to do something so I figured why not build something I can use and learn something new in the process. At that point in time I was doing a decent amount of freelance work and being the cheap bastard that I am didn’t want to pay a service like Freshbooks to actually handle my invoices for me so I decided to write my own. From start to finish it only took a couple of months working on the train only and during the process I decided to build it in a fashion that anyone could use it once it was done. I never thought that it could actually catch on or that people might actually use it for real invoices. Boy was I wrong.

Later that evening when I arrived home I checked the server and for some reason apache was not running. I was probably messing with it last week and forget to fix an error in the config but a few keystrokes and she was up and running again. To close the loop I shot the women a quick email and she replied thanking me. Tired from a long vacation I crashed.

The next day while in the shower I thought about Invoice Journal again. I wondered, how many people are actually using the system still? Do people still sign up for accounts? I set a reminder(thanks Siri) to check these stats later that night. A few sql queries later and to my absolute amazement this is was I saw.

Invoice journal stats

Not only were people still using it consistently to invoice clients but 130+ people on average were still signing up each month with zero advertising, zero promotional emails, zero effort at all. WTF? I hadn’t touched this code in over 4 years(4 years, 2 months, and 12 days to be exact), how is it possible that people still signup and use this after all this time. It must have major bugs in it, it must be missing major features that other services offer…this was crazy! Now I’m wondering if I had continued to work on it, if I hadn’t neglected it could it actually be a useful successful, dare I say profitable project?

Now, as for why this is happening I can only guess that I’m offering something a lot of budget conscious people(freelancers) are looking for. I’m really curious to pull some additional metrics like who has the most invoices, what’s the longest running user, and what’s the total dollar amount processed but I’ll save that for another night.

Unfortunately, I don’t have time to fix it up. It’s been way too long and I’ve moved on from Perl(I’m a Python boy now). I’d almost be afraid to look at the code and fix things for fear of breaking it for the people that are actively using it. As far as I can tell I have two options; I can let it run as is or I can find someone eager to take it over….any takers? :-)

Pandora, Your iPhone App Sucks in the Car, Here Is the Code to Fix It

I can’t be the first person to notice this and I’m really surprised that a public company(or am I?) has taken this long to update their app so that it’s even close to their competition. Anyway, let me explain my three pet peeves with the Pandora app while in the car.

First, the damn interface buttons are too small and too close together for quick glances. A day doesn’t go by where I don’t pause instead of skip or skip(Arg!) instead of pause. Since there is no tactical feedback on the iPhone I have to physically look down at the device to change songs; Bad idea when I’m cruising 80 mph down the Mass Pike. Instead why don’t you offer swipe to skip and double tap to pause/play. This would allow me to skips songs and/or play/pause with not so much as a glance at the phone. Here’s the code to implement it.

This code is not complete. My Objective-C is a little rusty but it should get the point across on how easy it is to implement these features. This code was not tested so it might not even work. :-P

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- (void)viewDidLoad{
    //...Your other magical code

    UISwipeGestureRecognizer *rightRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipe:)];
    rightRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
    [self.view addGestureRecognizer:rightRecognizer];
    [rightRecognizer release];

    UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];
    [doubleTap setNumberOfTapsRequired:2];
    [self.view addGestureRecognizer:doubleTap];
    [doubleTap release];
}

- (void)handleSwipe:(UISwipeGestureRecognizer*)gestureRecognizer {
    // Skip!
}

-(void)handleDoubleTap:(UISwipeGestureRecognizer*)gestureRecognizer{
    //If currently playing
        // Pause
    //Else
        // Play
}

The other thing that really grinds my gears is when you pop up that damn “Sorry, our music licenses…” alert. Sure I get why you have to alert me that I’ve skipped too many times but the alert interface(again while driving) is horrible. I skip(while trying to avoid the play button) and nothing happens. Damn, I have to look down again and I see the message and now I have to look even longer and aim for the OK button to dismiss it. Pain in the ass! A simple audible alert and a modal that fades away would be perfect. Here’s the code, help yourself.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
-(void)handleLicenseWarning{
    /* You already have this function, change that damn alert to this */

    NSURL *soundFileURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"bong.mp3" ofType:@"mp3"]];
    AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error:nil];
    [player play];

    UIView *licenseWarning = [[UIView alloc] initWithFrame:CGRectMake(self.view.frame.size.width/2-100,
                                                                                                                                            self.view.frame.size.width/2-50,
                                                                                                                                            200, 100)];
    [licenseWarning setBackgroundColor:[UIColor darkGrayColor]];
    [licenseWarning setAlpha:0.8];
    [licenseWarning.layer setCornerRadius:8];
    [licenseWarning.layer setMasksToBounds:YES];
    [licenseWarning setTag:123];

    UILabel *warning = [[UILabel alloc] initWithFrame:CGRectMake(10,10,180,80)];
    [warning setBackgroundColor:[UIColor clearColor]];
    [warning setText:@"Blah blah, our license..."];
    [warning setTextColor:[UIColor whiteColor]];
    [licenseWarning addSubview:warning];
    [warning release];

    [self.view addSubview:licenseWarning];
    [licenseWarning release];

    [NSTimer scheduledTimerWithTimeInterval:4
                                          target:self
                                        selector:@selector(hideWarning)
                                        userInfo:nil
                                         repeats:NO];
}

-(void)hideWarning{
    [UIView animateWithDuration:0.5 animations:^{
        [[self.view viewWithTag:123] setAlpha:0.0];
    } completion:^(BOOL b){
        [[self.view viewWithTag:123] removeFromSuperview];
    }];
}

I’ll expect these features in your next release which is coming any day now, right?

ios

Getting Gzip to Work With Amazon’s CloudFront and Nginx

I spent way too much time trying to figure out why CloudFront wouldn’t accept and serve my gzip’d content so I’m posting this hoping that I can save someone else some time.

Instead of diving into the issues with cloudfront and nginx I’ll just provide you with the settings that ended up working for me. The two settings to make note of are the gzip_http_version which is set to 1.0 and the gzip_proxied setting which is set to any. Buffers, comp_level and types can all be changed to fit what you’re trying to serve.

1
2
3
4
5
6
7
gzip on;
gzip_http_version 1.0;
gzip_buffers    4 8k;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 9;
gzip_types text/plain text/html text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript image/png image/gif image/jpeg;

AWS Security Group Cleanse

One afternoon while I was adding an ip to our AWS security group I noticed that we had almost 70 rules listed under one group most of which I had no clue what they were for. Over the past two years rules have been added as new servers were spun up, office ips changed and various employee added their home ip addresses. As part of our security month I set out to clean this up and maker sure it stays clean by automating the process.

Amazon’s interface for managing this is severely lacking mainly because they’ve built a system on APIs and thus the power comes from taping into those. My plan for this is to write a python script that when run will revoke old rules and add new ones based off the rules defined in the file. There will be three sections; one for ssh access which will be where developers deposit their ips, one for local servers which should only be used for other AWS instances and one for publicly accessible ports(web,ssl, etc)

Here’s how the rules will be laid out in the file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
## Add your home ip addresses here
ALLOWED_SSH = ['23.xx.xx.xx', ## Office
                              ]

## Server IPs that are given full access
INTERNAL = ['10.xxx.xx.xxx', # misc
            '10.xxx.xx.xxx', # static
            '10.xxx.xx.xxx', # solr
            '10.xxx.xx.xxx'  # www
           ]

PRODUCTION_RULES = [
    Rule("tcp", "80",  "80", "0.0.0.0/0", None),    # web
    Rule("tcp", "443", "443", "0.0.0.0/0", None),   # ssl 
    Rule("tcp", "8080", "8080", "0.0.0.0/0", None), # solr
]

The plan is to join these rules and revoke rules no longer in the list and authorize new ones that were added. By breaking it up like this and allowing comments it should be easy to keep our security group clutter free.

Here is the full script that runs nightly.

(aws-security.py) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import collections
import boto

group = 'ProductionSecurity'
dry_run = True

if dry_run:
    print "** DRY RUN **\nNo changes will be made"

Rule = collections.namedtuple("rules", ["protocol",
                                        "from_port",
                                        "to_port",
                                        "ip",
                                        "group_name"])

## these ips are allowed to ssh into all machines
ALLOWED_SSH = ['23.xx.xx.xx', ## Office
               ]

## Server IPs that are given full access
INTERNAL = ['10.xxx.xx.xxx', # misc
            '10.xxx.xx.xxx', # static
            '10.xxx.xx.xxx', # solr
            '10.xxx.xx.xxx'  # www
            ]

PRODUCTION_RULES = [
    Rule("tcp", "80",  "80", "0.0.0.0/0", None),    # web
    Rule("tcp", "443", "443", "0.0.0.0/0", None),   # ssl 
    Rule("tcp", "8080", "8080", "0.0.0.0/0", None), # solr
]

for ip in ALLOWED_SSH:
    PRODUCTION_RULES.append(Rule("tcp","22","22","%s/32" % ip,None))

for ip in INTERNAL:
    PRODUCTION_RULES.append(Rule("tcp","0","65535","%s/32" % ip,None))

conn = boto.connect_ec2()
groups = [g for g in conn.get_all_security_groups() if g.name == group]
group = groups[0]

existing = []
for rule in group.rules:
    for ip in rule.grants:
        rule_check = Rule(rule.ip_protocol,
                                    rule.from_port,
                                    rule.to_port,
                                    "%s" % ip,
                                    None)
        if rule_check not in PRODUCTION_RULES:
            if rule.ip_protocol == "tcp":
                print "Revoking %s (%s-%s)" % (ip,rule.from_port,rule.to_port)
                if not dry_run:
                    group.revoke(ip_protocol=rule.ip_protocol,
                                from_port=rule.from_port,
                                to_port=rule.to_port,
                                cidr_ip=ip,
                                src_group=None)
        else:
            existing.append(rule_check)

## Fill in missing rules 
for rule in PRODUCTION_RULES:
    if rule not in existing:
        print "Authorizing %s (%s-%s)" % (rule.ip,rule.from_port,rule.to_port)
        if not dry_run:
            group.authorize(ip_protocol=rule.protocol,
                            from_port=rule.from_port,
                            to_port=rule.to_port,
                            cidr_ip=rule.ip,
                            src_group=None)

TODO

One flaw with this system is you have to have access to the code to add an ip. An issue I’d like to solve soon is when you need to add an ip temporarily(coffee shop, airport, hotel, etc). Ideally I’d like to add a system where you email and ip to a certain address and it adds it for you either temporarily or permanently depending on what address you sent it to.

There is also no reason besides laziness that all servers should have all ports open internally. You’ll noticed that all the ips in the INTERNAL list get full access to the other servers which is not a good idea. I should change this to list out what ports should be open and for which servers. For example we probably only need to open ssh, mysql, memcached and redis between the servers.

How All Disputes Should Be Settled.

Quick 3 challenge

Code Sharing Dilemma.

Should I share code when I know it’s not going to add any value to the Internet and only clutter it with more useless spam content?

An old colleague contacted me recently congratulating me on my cool script I wrote. We wont get into the details of the script but it was a bot that performed a certain task and since this person is following me he was able to spot it. Since I’ve worked with him in the past and he mentioned he’s been trying to do something similar I offered up the code. I thought he was going to use it for his job, maybe they were trying to do something similar to what we’re doing so there is no harm in that. After I sent off the code he messaged me back thanking me and telling me his true intentions. He planned on using the script to basically spam the Internet with useless pages filled with ads. Ugh. Maybe I should’ve asked him what he was going to use it for first; maybe I shouldn’t care but either way I couldn’t help but feel responsible for cluttering up the Internet with more useless content. I wish him luck on his ventures but going forward I’m probably going to be more reluctant to share clever code so I can avoid such moral dilemmas in the future.