Archive

For May, 2011

When GPG is not the answer…

No Comments

When researching prior work on encrypted dump(8) files I kept coming back to the same idea – just encrypt the dump file with gpg.

This does not work!

I mean, yeah, it will encrypt your backups. But it has several problems.

1) you can’t stream it. This isn’t an issue when you create a file that you then burn to CD/DVD but some people still stream to tape.

2) you have to decrypt everything to retrieve one file. This isn’t an issue if you have a small incremental dump file but if you have a 4 GB compressed level-0 dump? That will take a while.

3) you lose everything if there’s a single bit error.

GPG is great but it’s just a tool. You have to choose the right tool for your problem. That means knowing when file-level encryption isn’t the right answer, specifically when you’re your encrypting isn’t a single logical entity like an individual document.

Buffer Encryption with OpenSSL

No Comments

Sometimes it’s the simplest tasks that remind you how of how much you’ve taken for granted when you return to a prior language. Well, not that encryption is easy….

The code below is my pre-beta code from native encryption support in dump/restore. For that application it will process standalone chunks of 10kB although my unit tests have gone as large as a quarter megabyte. The final version may use the OpenPGP format (RFC 2440) for the blocking – I haven’t made a final decision yet.

The code passes unit tests but I haven’t taken the time to make it pretty. The ‘transformation’ type below is just a simple placeholder for some crypto structures and is easily inferred.

REMINDER: always observe local laws regarding cipher strength!

Encryption

For encryption we first compress the data to increase its entropy and reduce the amount of material we must encrypt. (Compression is far cheaper, computationally, than software encryption.) We then append the hash of the uncompressed data so we can be sure we properly decrypted and decompressed it. Finally everything is encrypted.

  1. ssl_compress(Transformation *xform, char *dest,
  2.     unsigned long *destlen, const char *src, int srclen)
  3. {
  4.     unsigned char salt[16], iv[EVP_MAX_MD_SIZE];
  5.     unsigned int saltlen = sizeof(salt);
  6.     unsigned int ivlen = sizeof(iv);
  7.     unsigned char digest[EVP_MAX_MD_SIZE];
  8.     unsigned int digestlen = 0;
  9.     char *buf;
  10.     unsigned int len, len2;
  11.  
  12.     len = srclen + 1000;
  13.     buf = malloc(len);
  14.     digestlen = sizeof(digest);
  15.  
  16.     // generate salt, put it in header
  17.     generateIV(xform, salt, &saltlen, iv, &ivlen);
  18.     memcpy(tpbin->buf, salt, saltlen);
  19.  
  20.     // compress the buffer first - increase the entropy
  21.     int compresult;
  22.     compresult = compress2(buf, destlen, src, srclen, xform->state.ssl.complvl);
  23.     if (compresult != Z_OK) {
  24.         printf("unable to compress...\n");
  25.             return 0;
  26.     }
  27.  
  28.     EVP_EncryptInit_ex(xform->state.ssl.dataCtx, xform->state.ssl.cipher,  xform->state.ssl.engine, NULL, NULL);
  29.     //EVP_CIPHER_CTX_set_key_length(&ctx, 8); - some ciphers allow different key lengths
  30.     EVP_EncryptInit_ex(xform->state.ssl.dataCtx, NULL, NULL, xform->state.ssl.key, iv);
  31.  
  32.     // encrypt content.
  33.     if (!EVP_EncryptUpdate(xform->state.ssl.dataCtx, tpbin->buf + saltlen, &len, buf, *destlen)) {
  34.         EVP_CIPHER_CTX_cleanup(xform->state.ssl.dataCtx);
  35.         ERR_print_errors_fp(stdout);
  36.         free(buf);
  37.         return 0;
  38.     }
  39.  
  40.     // calculate digest, add it.
  41.     EVP_Digest(src, srclen, digest, &digestlen, xform->state.ssl.digest,  xform->state.ssl.engine);
  42.  
  43.     len2 = *destlen - len - saltlen;
  44.     if (!EVP_EncryptUpdate(xform->state.ssl.dataCtx, tpbin->buf + saltlen + len, &len2, digest, digestlen)) {
  45.         EVP_CIPHER_CTX_cleanup(xform->state.ssl.dataCtx);
  46.         ERR_print_errors_fp(stdout);
  47.         free(buf);
  48.         return 0;
  49.     }
  50.  
  51.     len += len2;
  52.  
  53.     // finish up
  54.     len2 = *destlen - len - saltlen;
  55.     if (!EVP_EncryptFinal(xform->state.ssl.dataCtx, tpbin->buf + saltlen + len, &len2)) {
  56.         EVP_CIPHER_CTX_cleanup(xform->state.ssl.dataCtx);
  57.         ERR_print_errors_fp(stdout);
  58.         free(buf);
  59.         return 0;
  60.     }
  61.  
  62.     *destlen = len + len2 + saltlen;
  63.     free(buf);
  64.  
  65.     OPENSSL_cleanse(iv, sizeof iv);
  66.  
  67.     return 1;
  68. }
ssl_compress(Transformation *xform, char *dest,
	unsigned long *destlen, const char *src, int srclen)
{
    unsigned char salt[16], iv[EVP_MAX_MD_SIZE];
    unsigned int saltlen = sizeof(salt);
    unsigned int ivlen = sizeof(iv);
    unsigned char digest[EVP_MAX_MD_SIZE];
    unsigned int digestlen = 0;
    char *buf;
    unsigned int len, len2;

    len = srclen + 1000;
    buf = malloc(len);
    digestlen = sizeof(digest);

    // generate salt, put it in header
    generateIV(xform, salt, &saltlen, iv, &ivlen);
    memcpy(tpbin->buf, salt, saltlen);

    // compress the buffer first - increase the entropy
    int compresult;
    compresult = compress2(buf, destlen, src, srclen, xform->state.ssl.complvl);
    if (compresult != Z_OK) {
        printf("unable to compress...\n");
            return 0;
    }

    EVP_EncryptInit_ex(xform->state.ssl.dataCtx, xform->state.ssl.cipher,  xform->state.ssl.engine, NULL, NULL);
    //EVP_CIPHER_CTX_set_key_length(&ctx, 8); - some ciphers allow different key lengths
    EVP_EncryptInit_ex(xform->state.ssl.dataCtx, NULL, NULL, xform->state.ssl.key, iv);

    // encrypt content.
    if (!EVP_EncryptUpdate(xform->state.ssl.dataCtx, tpbin->buf + saltlen, &len, buf, *destlen)) {
        EVP_CIPHER_CTX_cleanup(xform->state.ssl.dataCtx);
        ERR_print_errors_fp(stdout);
        free(buf);
        return 0;
    }

    // calculate digest, add it.
    EVP_Digest(src, srclen, digest, &digestlen, xform->state.ssl.digest,  xform->state.ssl.engine);

    len2 = *destlen - len - saltlen;
    if (!EVP_EncryptUpdate(xform->state.ssl.dataCtx, tpbin->buf + saltlen + len, &len2, digest, digestlen)) {
        EVP_CIPHER_CTX_cleanup(xform->state.ssl.dataCtx);
        ERR_print_errors_fp(stdout);
        free(buf);
        return 0;
    }

    len += len2;

    // finish up
    len2 = *destlen - len - saltlen;
    if (!EVP_EncryptFinal(xform->state.ssl.dataCtx, tpbin->buf + saltlen + len, &len2)) {
        EVP_CIPHER_CTX_cleanup(xform->state.ssl.dataCtx);
        ERR_print_errors_fp(stdout);
        free(buf);
        return 0;
    }

    *destlen = len + len2 + saltlen;
    free(buf);

    OPENSSL_cleanse(iv, sizeof iv);

    return 1;
}

Decryption

The decryption process reverses what I did before. I decrypt the entire block and then attempt to decompress it. Finally I compute the hash on the decompressed data and compare it to what I saved earlier.

  1. ssl_decompress(Transformation *xform, struct tapebuf *tpbin,
  2.     unsigned long *destlen, const char *src, int srclen, char **reason)
  3. {
  4.     unsigned char salt[16], iv[EVP_MAX_MD_SIZE];
  5.     unsigned int saltlen = sizeof(salt);
  6.     unsigned int ivlen = sizeof(iv);
  7.     unsigned char digest[EVP_MAX_MD_SIZE];
  8.     unsigned int digestlen;
  9.     char *buf;
  10.     unsigned int len, len2;
  11.  
  12.     digestlen = EVP_MD_size(xform->state.ssl.digest);
  13.  
  14.     len = *destlen + 1000;
  15.     buf = malloc(len);
  16.  
  17.     // how to know salt length?
  18.     memcpy(salt, src, saltlen);
  19.     generateIV(xform, salt, &saltlen, iv, &ivlen);
  20.  
  21.     EVP_DecryptInit_ex(xform->state.ssl.dataCtx, xform->state.ssl.cipher,  xform->state.ssl.engine, NULL, NULL);
  22.     //EVP_CIPHER_CTX_set_key_length(&ctx, 8);
  23.     EVP_DecryptInit_ex(xform->state.ssl.dataCtx, NULL, NULL, xform->state.ssl.key, iv);
  24.  
  25.     if (!EVP_DecryptUpdate(xform->state.ssl.dataCtx, buf, &len, src+saltlen, srclen-saltlen)) {
  26.         EVP_CIPHER_CTX_cleanup(xform->state.ssl.dataCtx);
  27.         free(buf);
  28.         ERR_print_errors_fp(stdout);
  29.         return 0;
  30.     }
  31.  
  32.     len2 = *destlen + 1000 - len;
  33.     if (!EVP_DecryptFinal(xform->state.ssl.dataCtx, buf + len, &len2)) {
  34.         EVP_CIPHER_CTX_cleanup(xform->state.ssl.dataCtx);
  35.         free(buf);
  36.         ERR_print_errors_fp(stdout);
  37.         return 0;
  38.     }
  39.     len += len2;
  40.     len -= digestlen;
  41.  
  42.     OPENSSL_cleanse(iv, sizeof iv);
  43.  
  44.     int cresult;
  45.     cresult = uncompress(tpbin->buf, destlen, buf, len);
  46.     switch (cresult) {
  47.         case Z_OK:
  48.             *reason = "";
  49.             break;
  50.         case Z_MEM_ERROR:
  51.             *reason = "not enough memory";
  52.             break;
  53.         case Z_BUF_ERROR:
  54.             *reason = "buffer too small";
  55.             break;
  56.         case Z_DATA_ERROR:
  57.             *reason = "data error";
  58.             break;
  59.         default:
  60.             *reason = "unknown";
  61.     }
  62.     if (cresult != Z_OK) {
  63.         printf("compression failed: %s\n", *reason);
  64.         free(buf);
  65.         return 0;
  66.     }
  67.  
  68.     // verify digest
  69.     EVP_Digest(tpbin->buf, *destlen, digest, &digestlen, xform->state.ssl.digest,  xform->state.ssl.engine);
  70.  
  71.     if (memcmp(buf + len, digest, digestlen)) {
  72.         *reason = "digests did not match";
  73.         return 0;
  74.     }
  75.  
  76.     free(buf);
  77.  
  78.     return 1;
  79. }
ssl_decompress(Transformation *xform, struct tapebuf *tpbin,
    unsigned long *destlen, const char *src, int srclen, char **reason)
{
    unsigned char salt[16], iv[EVP_MAX_MD_SIZE];
    unsigned int saltlen = sizeof(salt);
    unsigned int ivlen = sizeof(iv);
    unsigned char digest[EVP_MAX_MD_SIZE];
    unsigned int digestlen;
    char *buf;
    unsigned int len, len2;

    digestlen = EVP_MD_size(xform->state.ssl.digest);

    len = *destlen + 1000;
    buf = malloc(len);

    // how to know salt length?
    memcpy(salt, src, saltlen);
    generateIV(xform, salt, &saltlen, iv, &ivlen);

    EVP_DecryptInit_ex(xform->state.ssl.dataCtx, xform->state.ssl.cipher,  xform->state.ssl.engine, NULL, NULL);
    //EVP_CIPHER_CTX_set_key_length(&ctx, 8);
    EVP_DecryptInit_ex(xform->state.ssl.dataCtx, NULL, NULL, xform->state.ssl.key, iv);

    if (!EVP_DecryptUpdate(xform->state.ssl.dataCtx, buf, &len, src+saltlen, srclen-saltlen)) {
        EVP_CIPHER_CTX_cleanup(xform->state.ssl.dataCtx);
        free(buf);
        ERR_print_errors_fp(stdout);
        return 0;
    }

    len2 = *destlen + 1000 - len;
    if (!EVP_DecryptFinal(xform->state.ssl.dataCtx, buf + len, &len2)) {
        EVP_CIPHER_CTX_cleanup(xform->state.ssl.dataCtx);
        free(buf);
        ERR_print_errors_fp(stdout);
        return 0;
    }
    len += len2;
    len -= digestlen;

    OPENSSL_cleanse(iv, sizeof iv);

    int cresult;
    cresult = uncompress(tpbin->buf, destlen, buf, len);
    switch (cresult) {
        case Z_OK:
            *reason = "";
            break;
        case Z_MEM_ERROR:
            *reason = "not enough memory";
            break;
        case Z_BUF_ERROR:
            *reason = "buffer too small";
            break;
        case Z_DATA_ERROR:
            *reason = "data error";
            break;
        default:
            *reason = "unknown";
    }
    if (cresult != Z_OK) {
        printf("compression failed: %s\n", *reason);
        free(buf);
        return 0;
    }

    // verify digest
    EVP_Digest(tpbin->buf, *destlen, digest, &digestlen, xform->state.ssl.digest,  xform->state.ssl.engine);

    if (memcmp(buf + len, digest, digestlen)) {
        *reason = "digests did not match";
        return 0;
    }

    free(buf);

    return 1;
}

Initial Vector (IV) generation

It is important to create good Initial Vectors (IVs). This method creates an IV from a hash of the salt and a function of the salt + key.

  1. generateIV(Transformation *xform, unsigned char *salt, unsigned int *saltlen,
  2.     unsigned char *iv, unsigned int *ivlen)
  3. {
  4.     unsigned char ivbuffer[64];
  5.     unsigned int buflen, y;
  6.  
  7.     // we can use pseudorandom bytes since they're going
  8.     // to be exposed to any attacker anyway.
  9.     *saltlen = 16;
  10.     if (xform->enc == 1) {
  11.         RAND_pseudo_bytes(salt, *saltlen);
  12.     }
  13.  
  14.     memcpy(ivbuffer, salt, 16);
  15.  
  16.     // -decrypt- salt value
  17.     memset(ivbuffer, 0, sizeof(ivbuffer));
  18.     EVP_CipherInit_ex(xform->state.ssl.ivCtx, xform->state.ssl.cipher, xform->state.ssl.engine, NULL, NULL, 0);
  19.     //EVP_CIPHER_CTX_set_key_length(&ctx, 8);
  20.     EVP_CIPHER_CTX_set_padding(xform->state.ssl.ivCtx, 0);  // -nopad
  21.     EVP_CipherInit_ex(xform->state.ssl.ivCtx, NULL, NULL, xform->state.ssl.key, ivbuffer, 0);
  22.     buflen = 32;
  23.     if (!EVP_CipherUpdate(xform->state.ssl.ivCtx, ivbuffer + 16, &buflen, salt, 16)) {
  24.         y = 32 - buflen;
  25.         if (!EVP_CipherFinal(xform->state.ssl.ivCtx, ivbuffer + 16 + buflen, &y)) {
  26.             buflen += y;
  27.         } else {
  28.             memset(ivbuffer + 16, 0, 32);
  29.         }
  30.     } else {
  31.         memset(ivbuffer + 16, 0, 32);
  32.     }
  33.     memcpy(ivbuffer + 48, salt, 16);
  34.  
  35.     // now digest it.
  36.     EVP_Digest(ivbuffer, 64, iv, ivlen, xform->state.ssl.digest, NULL);
  37.  
  38.     return 0;
  39. }
generateIV(Transformation *xform, unsigned char *salt, unsigned int *saltlen,
    unsigned char *iv, unsigned int *ivlen)
{
    unsigned char ivbuffer[64];
    unsigned int buflen, y;

    // we can use pseudorandom bytes since they're going
    // to be exposed to any attacker anyway.
    *saltlen = 16;
    if (xform->enc == 1) {
        RAND_pseudo_bytes(salt, *saltlen);
    }

    memcpy(ivbuffer, salt, 16);

    // -decrypt- salt value
    memset(ivbuffer, 0, sizeof(ivbuffer));
    EVP_CipherInit_ex(xform->state.ssl.ivCtx, xform->state.ssl.cipher, xform->state.ssl.engine, NULL, NULL, 0);
    //EVP_CIPHER_CTX_set_key_length(&ctx, 8);
    EVP_CIPHER_CTX_set_padding(xform->state.ssl.ivCtx, 0);	// -nopad
    EVP_CipherInit_ex(xform->state.ssl.ivCtx, NULL, NULL, xform->state.ssl.key, ivbuffer, 0);
    buflen = 32;
    if (!EVP_CipherUpdate(xform->state.ssl.ivCtx, ivbuffer + 16, &buflen, salt, 16)) {
        y = 32 - buflen;
        if (!EVP_CipherFinal(xform->state.ssl.ivCtx, ivbuffer + 16 + buflen, &y)) {
            buflen += y;
        } else {
            memset(ivbuffer + 16, 0, 32);
        }
    } else {
        memset(ivbuffer + 16, 0, 32);
    }
    memcpy(ivbuffer + 48, salt, 16);

    // now digest it.
    EVP_Digest(ivbuffer, 64, iv, ivlen, xform->state.ssl.digest, NULL);

    return 0;
}

Rethinking ‘dump’

No Comments

Backup programs are interesting critters. You need one that backs up what you do but you also end up only doing things that you can back up. The most commonly used formats, tar and zip, are great for what they do but there are some serious limitations for a modern system.

  • Sparse File – a sparse file allows you to create a file that seems to be quite large but that only actually consumes disk space when you write data to it. A good modern use is creating a disk image – you can create a  lage sparse file, put a filesystem on it, and only require a bit more disk space than the files you put in it. The file will expand to the nominal size when you burn it to a CD or DVD but you want your backups to be more memory-efficient.
  • Extended Attributes – boolean extended attributes beyond the standard Unix discretionary attributes (e.g., read/write/execute). Some examples are:
    • immutable - nobody can modify the file, not even root.
    • append-only – the file cannot be modified other than appending data to it. You’ll often see this attribute on log files.
    • secure undelete – the disk sectors are zeroed out when the file is deleted
    • do not backup – don’t back up this file. You’ll often see this attribute on private key files.
  • Access Control Lists – extended attributes that provide finer access control than the standard Unix controls. E.g., you can say that a file is read-only except for user ids 1003, 1073 and 1083 who can read it and user 1077 can’t read the file at all.
  • Mandatory Access Control Labels (SELinux) – these are mandatory access control labels. E.g., you can label a file to say that it’s used by the web server and all of the policies associated with the web server should be applied to it.

Many if not all of these attributes are being standardized but there’s no support in (standard) tar and zip formats. We don’t need this functionality on the typical home system but they can be critical on servers.

So what’s wrong with ‘dump’ for Ext2/3/4 filesystems?

  • No Indexing – it should be possible to quickly determine whether a file is present in an archive and to retrieve it. ‘Dump’ provides limited support with a proprietary data format but it’s not easily to create an index spanning multiple archives.
  • No Error Detection – there is no way to determine that an archived file has been corrupted.
  • No Encryption – there is no native encryption for the archives. This is important if you write your archive directly to tape or are unable to load the complete archive for decryption before restoring files.

There is one additional issue when performing disk-based backups – a modern kernel will cache a great deal of information and the raw block device may not be fully consistent. LVM-based partitions will help tremendously – we can sync(1) the filesystem and immediately create a snapshot. Applications may still have unwritten caches but we can’t do anything about that without taking the system down to a quiet state and unmounting the partition.

Important: do not attempt to create file-based backups of running databases! Use the backup program provided by the database if you need to back up a running database.

So why doesn’t anyone do something about this? Funny you should ask…. In fact I’ve started working on this and hope to submit a patch to the maintainers soon.  The first patch will provide a SQLite index to the archive in addition to the existing format. The second patch will provide error detection.

Encryption support is much more difficult. It’s easy to do something, but it’s also easy to screw up and have a much weaker system than you realized.

Configuring Ubuntu 11.04 To Send Mail via Google Apps

No Comments

Like many people I’ve abandoned running my own mail servers due to the sheer volume of spam and malware.  Google Apps provides a nice solution – to the rest of the world I’m still running my own servers but Google deals with the spam, the sender authentication records, etc.

Aside: I can’t say too many good things about Google Apps. It is free for less than 10 users (according to recent email) and educational facilities and only $50/user/year for the business version. You aren’t even forced to use the webmail interface – it supports both IMAP and POP so you can use a traditional standalone mail application like thunderbird or evolution.

One big downside is that locally generated mail tended to get lost. I think of it as jurisdictional issues – my mail server knows my domain and doesn’t think it needs to send the mail to another system. It handles my domain! There’s probably a simple solution to this but I’ve never taken the time to search for it.

Fortunately it’s easy with Ubuntu 11.04.

  1. Install postfix if it isn’t installed. In this case specify a satellite site configuration.
  2. Edit /etc/postfix/main.cf and add the following lines:
    1. # comment out the following line - we want all mail to be forwarded
    2. #mydestination = domain, hostname, localhost.localdomain, etc.
    3. relayhost = [smtp.gmail.com]:587
    4. smtp_sasl_auth_enable = yes
    5. smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
    6. smtp_sasl_mechanism_filter = login
    7. smtp_sasl_security_options = noanonyous
    8. smtp_sasl_tls_security_options = noanonymous
    9. smtp_use_tls = yes
    10. # the next few items force the use of TLS encryption on outbound email
    11. smtp_tls_per_site = hash:/etc/postfix/tls_per_site
    12. # this will allow postfix to talk to any site Firefox recognizes
    13. #smtp_tls_CApath = /usr/share/ca-certificates/mozilla
    14. # this is much more restrictive - it will recognize GA/gmail.
    15. smtp_tls_CAfile = /usr/share/ca-certificates/mozilla/Equifax
    # comment out the following line - we want all mail to be forwarded
    #mydestination = domain, hostname, localhost.localdomain, etc.
    relayhost = [smtp.gmail.com]:587
    smtp_sasl_auth_enable = yes
    smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
    smtp_sasl_mechanism_filter = login
    smtp_sasl_security_options = noanonyous
    smtp_sasl_tls_security_options = noanonymous
    smtp_use_tls = yes
    # the next few items force the use of TLS encryption on outbound email
    smtp_tls_per_site = hash:/etc/postfix/tls_per_site
    # this will allow postfix to talk to any site Firefox recognizes
    #smtp_tls_CApath = /usr/share/ca-certificates/mozilla
    # this is much more restrictive - it will recognize GA/gmail.
    smtp_tls_CAfile = /usr/share/ca-certificates/mozilla/Equifax
  3. Create the /etc/password/sasl_passwd file and add your GA (or gmail) credentials. IMPORTANT: as far as GA (or gmail) is concerned all of the mail sent from this system comes from you so you will want to take a different approach if multiple people use this system! Specifically you’ll want to look at setting up an ‘outbound gateway’ via Google Apps for Business or Education.
    1. [smtp.gmail.com]:587 user@mydomain.com:password
    [smtp.gmail.com]:587 user@mydomain.com:password
  4. Create the /etc/postfix/tls_per_site file and specify that mail sent to GA (or gmail) MUST be encrypted. This map allows us to use different policies if we have explicit ‘transport’ entries for other destinations.
    1. [smtp.gmail.com]:587 MUST
    [smtp.gmail.com]:587 MUST
  5. Create the hash files and change the permissions on the password files.
    1. $ postmap /etc/postfix/sasl_passwd
    2. $ chmod 0640 /etc/postfix/sasl_passwd*
    3. $ postmap /etc/postfix/tls_per_site
    $ postmap /etc/postfix/sasl_passwd
    $ chmod 0640 /etc/postfix/sasl_passwd*
    $ postmap /etc/postfix/tls_per_site
  6. Make a small change in /etc/postfix/master.cf around line 36.
    1. smtp      unix  -       -       <strong>n</strong> -       -       smtp# When relaying mail as backup MX, disable fallback_relay to avoid MX loops
    2. relay     unix  -       -       <strong>n</strong> -       -       smtp        -o smtp_fallback_relay=
    smtp      unix  -       -       <strong>n</strong> -       -       smtp# When relaying mail as backup MX, disable fallback_relay to avoid MX loops
    relay     unix  -       -       <strong>n</strong> -       -       smtp        -o smtp_fallback_relay=
  7. Add yourself to /etc/aliases.
    1. # See man 5 aliases for format
    2. postmaster:    root
    3. root: user@mydomain.com
    # See man 5 aliases for format
    postmaster:    root
    root: user@mydomain.com
  8. Verify that everything is okay, rebuild the alias file and restart the server.
    1. $ sudo postfix check
    2. $ sudo newaliases
    3. $ sudo service postfix restart
    $ sudo postfix check
    $ sudo newaliases
    $ sudo service postfix restart

Improving Security: Restricting Certificate Authorities

Adding TLS encryption is only half of the problem. We need to make sure that we’re actually talking to GA/gmail. For that we need to verify the GA/gmail certificate – in fact postfix will refuse to deliver the mail if it can’t verify the certificate of the remote system.

This is simple in concept but it requires us to trust the certificate authorities that sign these certificates. Firefox (and Debian/Ubuntu) vet them but the nature of the current certificate authority architecture means that ANY CA can sign a certificate for ANY server. This means that some obscure CA in Unfreedonia under government pressure can sign a certificate for ‘smtp.gmail.com’ and it will be accepted by postfix as long as that certificate is in the CApath.

We can improve the situation by specifying only the file used by GA/gmail’s certificate. We can get that this by looking at the postfix logs and a bit of experimentation. I gave the appropriate CAfile above.

As an aside: we can’t delete the standard CAs in either firefox or chrome but we can remove permissions so we can greatly reduce the number of CAs we trust. I blogged about this a few months ago.

Obvious-in-Hindsight Linux Recovery Preparation

No Comments

Today’s “whack yourself on the forehead and say ‘why didn’t I think of that?!’” moment…

Don’t just put the kernel images in your /boot partition. Put an Ubuntu server instance in that partition. THEN create the desktop installation on the rest of the disk. You need to do a little bit of work to merge the two GRUB configurations (and I’ll admit I haven’t done this yet) but when you’re done you have a fully functional second Ubuntu installation that will be around in case you screw up your desktop installation. You won’t have the GUI but that shouldn’t be a problem if all you’re doing is repairing the desktop instance.

Why not just use a rescue CD? Two reasons. First, you may not have one handy. Second, the stock rescue CDs don’t know the details of your installation. Keeping a server instance in your /boot partition means you can also perform critical backups to that partition and have it handy in case things go wrong. You could create a custom rescue CD/USB stick with the local details but review the first point again.

(It never ceases to amaze me that I constantly run across packages of unopened USB sticks when I’m not looking for one but the instant I need one they all disappear.)

There’s potentially a second benefit to having this instance. If you’re potentially compromised you should boot from a CD and mount the partition(s) read-only/noexec before running your checks.1 In the real world it’s enough for most people to just boot into this server instance and work from it. If there’s a problem you’ll want to reinstall the OS anyway.2

(You do have a separate /home partition so reinstallation of the OS is relatively painless, right?)

1. If there’s the remotest possibility of this going to court you need to remove the media, set it to read-only (via the jumpers) if possible, then attach it to another system just long enough to create a mirror of the entire disk before sealing the drive and handing it to a lawyer or other secure facility for storage. PATA cables should have the ‘write’ line cut, I don’t know what you can do with SATA cables. Ideally all done by a properly certified and disinterested third party.

In practice nobody but a large company or someone specializing in computer security has the resources to take a matter to court.

2. Never try to ‘fix’ a compromised system and never restore the OS from a backup. It’s too easy to miss something or restore a compromised system. Linux installation is a lot easier than it used to be and Ubuntu 11.04 introduced “.override” files that make it even easier.

This is THE reason for having a separate partition for /home and each of your services. It’s easy to trust separate backups and restorations if I have a separate /home partition that’s always mounted nosuid/noexec/nodev, If everything is in one big partition… system restorations after an OS reinstall are a lot more complicated.

Blue Taste Theme created by Jabox