Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Show HN: Cryptic – A sensible secret management toolkit and Go library (github.com/domodwyer)
93 points by domodwyer on Aug 21, 2016 | hide | past | favorite | 32 comments


So this is a neat idea, because Credstash and Sneaker are certainly not perfect gizmos, but it leads to the turtles problem: how do I securely handle the initial key that's being plopped on systems? If I can do that, I don't need Cryptic.

I've become a big fan of Credstash over the last few months largely because it solves (well, more like makes irrelevant, I wouldn't really strongly say it's "solved") this problem through using AWS as a trusted third party. Further, Credstash actually handles key ACLs in a much, much smarter way in that it uses KMS encryption contexts, which can be dropped into an IAM policy's Condition block, to further restrict access to secrets. Cryptic doesn't seem to offer this, and its use of Redis as a backing store (itself intended to be used in trusted environments) makes me worried, too.

If continuing along with this project--and please don't take this as discouraging, it seems largely reasonable as a thing!--I would look in more depth at how Credstash uses KMS and bloodily rip it off to improve Cryptic's KMS functionality; it's very, very good at what it's doing.


Credstash looks great, but having written something very similar for internal use (a secrets management system using KMS and S3 for use by EC2 instances) I was expecting it to have built in functionality to manage IAM policies the way you describe. Otherwise I'm not sure how this restricts different roles from accessing each other's secrets, which is the essential part of defense in depth for secret management.

For example, with S3, I have been defining IAM policies that restrict each role's access to just the secrets granted to that role, with a prefix-conditional IAM statement. You can further define similar parameterized policies for IAM users and groups without even enumerating their names.


As mentioned, KMS uses encryption contexts (a set of key-value pairs) to namespace your secrets. KMS won't decrypt the data key if you aren't in the key policy or your IAM policies don't allow access to that particular encryption context.

You're doing it at the S3 object level (presumably with a single KMS key?). Credstash does it at the KMS access level. Both are roughly equivalent, but Dynamo accesses are a lot cheaper in my neck of the woods than S3 accesses.


It seems to me that cost is negligible for any of the use cases. The KMS CMK cost will dominate access cost in my estimation. I see that Credstash cites latency as a factor in favor of S3, which is not a major factor if you retrieve a secret only once in a while and put it into memory, but could enable cool use cases where you really retrieve it on demand. S3 does have other advantages - ease of use and granularity of IAM access control, I think.

The Credstash documentation and code lead me to believe you are responsible for managing your own IAM policies - it won't set the policies in credential writer mode. Is that not correct? I expect this functionality to be part of the secret manager.


> I see that Credstash cites latency as a factor in favor of S3, which is not a major factor if you retrieve a secret only once in a while and put it into memory

Sorry, I wasn't clear. I meant "cheaper" in terms of time. I have a lot of machines in flight at any time, and I do actively roll credentials--chef-zero runs every five minutes and pulls credentials out of Credstash. S3 accesses add latency, which slows my converges (sometimes by a lot; I currently have around ~25 secret lookups in one particular Chef run).

> I expect this functionality to be part of the secret manager.

I'm not saying you're wrong to want that, but I'm very skeptical of it--extremely skeptical. I can't envision a situation where I'd rather have policy management as part of the secret manager as opposed to part of my provisioning infrastructure. My KMS accesses are in IAM roles; there is no situation in which I would ever want Credstash to manage those, because I have other stuff in them! I build my policies in CloudFormation, just like I do everything else.

If anything, my biggest criticism of Credstash is that it has a hardcoded table name that I'd rather was an environment variable so that I could establish that via CloudFormation and dump it into an env var via Chef.


In many cases, I think the secret manager should be part of your provisioning infrastructure. There are certainly other reasonable ways to structure this though - as long as you're clear on your threat model.

Would you agree that the secret manager should at least print out a suggested IAM policy for you to use? IAM policies are complex, tricky, and easy to mess up. I think it should absolutely be the job of the secret manager to at least sanity check your policies, if not write them directly.

> S3 accesses add latency [vs dynamodb], which slows my converges

That's useful to know, thanks.


I don't find IAM policies to be tricky, to be honest. It may just be familiarity; I have been doing this for a little bit of time now. I'm also not really sure what sort of interface for generating those policies without introducing to the secret manager its own notion of "policies"--at which point you're duplicating effort and work.

(I also wouldn't be able to use any policy spat out by the tool in the first place--I use a Cfer, Ruby DSL, to generate CloudFormation and all I'd be doing is transliterating it over.)


> I don't find IAM policies to be tricky, to be honest. It may just be familiarity; I have been doing this for a little bit of time now.

Then you are an elite AWS operator, but you also have to think about how to teach and review work from other, less skilled operators working with you. In my experience it's not easy at all to do that with something as critical as IAM, so when I'm orchestrating resources, I heavily rely on IAM policy automation and built-in sanity checks.


My policies are automated. I wrote the automation. :) And thank you for the compliment, but I wouldn't call myself "elite", merely "pretty good". Seriously, this is literally all it is:

   def kms_read_policy(contexts)
     contexts.map do |context|
       {
         PolicyName: "kms-read",
         PolicyDocument: {
           Version: '2012-10-17',
           Statement: [
             {
               Effect: 'Allow',
               Action: 'kms:Decrypt' ,
               Resource: [
                 Cfer.cfize('arn:aws:kms:C{AWS.region}:' \
                           'C{AWS.account_id}:key/C{Fn.ref(:CredstashKey)}')
               ],
               Condition: {
                 StringEquals: 
                   context.map do |k, v|
                     [ "kms:EncryptionContext:#{k}", v ]
                   end.to_h
               }
             }
           ]
         }
       }
     end.to_a
   end
And a single DynamoDB policy in a managed policy that allows for querying the `credential-store` table. This just is not rocket science. It's helped by cfer[0] and cfer-provisioning[1] being pretty great, but it's very achievable.

[0] - https://github.com/seanedwards/cfer

[1] - https://github.com/seanedwards/cfer-provisioning (I'm a maintainer)


I'm aware of the context parameter passed to KMS, and it is indeed something I plan on adding - one step at a time!

This was largely a side project that's gone quite well so far(!) - it's something I'll continue to add to. Any other suggestions are more than welcome.

Like it says in the readme, I just wanted a simple (single binary) deployment - it's in no way a dig at credstash.


I get you--I mean, if this was Credstash-compatible (using DynamoDB), I'd probably use it.


I don't know much about secret management. How does this compare to something like Vault?

Edit: https://www.vaultproject.io/


Can this be used to replace apps like 1Password for a skilled user?


That would be a bad idea, since (a) 1Password (at least in standalone mode) has been vetted crypographically, and (b) most of the security problems of password management aren't cryptographic.


thank you


This is exactly the use case of https://github.com/gtank/cryptopasta

Although, the only thing I can see it doing wrong is the use of passwords as keys https://github.com/domodwyer/cryptic/blob/6bd92fab6778dac26c... which is still an open issue in cryptopasta https://github.com/gtank/cryptopasta/issues/7


They're only there as an easy-to-read example, the library fully supports binary keys if that's what you're getting at?


A secure storage system shouldn't allow passwords as keys. If passwords are a thing that is going to happen, the API should be structured so that those passwords get run through a KDF. That's what he's getting at.


You're absolutely right.

PBKDF2 incoming.


You can do that, but a better API design is one that simply ensures keys are always random. KDF'd passwords are still weaker than real cryptographic keys.

A common design pattern is to use KDF keys as key-encrypting keys wrapping fully random keys. The system in steady-state relies only on the fully random key; the KDF key, depending on the design, can sometimes be kept offline. This is how SSH and GPG handle keys: note how the actual keys the system uses are fully random and totally out of the user's hands.

But you might not need any of this mechanism at all; it might be totally possible to design this system such that keys are simply blobs, generated via urandom by reading crypt.Rand.


I suspect the parent is wondering why it isn't using a KDF.

https://en.wikipedia.org/wiki/Key_derivation_function


I went with biscuit because it writes to a simple file and I am not worried about updating 2 secrets at once, particularly since I have a different file per app per environment. https://github.com/dcoker/biscuit/blob/master/README.md


Its interesting to see a lot of alternative implementations of credstash pop up, there was one written in Haskell recently called "credentials". https://github.com/brendanhay/credentials


It's always good to see options out there. But honestly, having being working with Hashicorp's Vault for a while (https://www.vaultproject.io/) I really don't see the need for it. Vault is extremely simple to setup and has many more advanced features without sacrificing functionality. There is also Keywhiz (https://square.github.io/keywhiz/single_page.html) which also provides a nifty FUSE abstraction.


The readme doesn't say what databases are supported, and similarly the config example config file doesn't have a field to configure this either.

The only clue is the example SQL for creating the table appears to be MySQL. Is Postgres supported?


The binaries don't support Postgres (though it's a 2 minute job to add) because the drivers aren't included in the build, though the library can support it.

The underlying SQL uses "?" as a parameter binding placeholder, Postgres uses $1, $2 right?

Seems like an obvious oversight, I can put up a build if you're willing to test it?


Cool! I've love to hear more about the security and crypto design. I started writing up some questions but after asking so many it feels like I should just check out the code and answer some of them for myself. Still, I left the discussion here in case you're interested :)

I've been working on a similar tool in Rust: https://github.com/ketralnis/secrets. My focus has been less APIs and more person-to-person (e.g. sharing the team's twitter password between humans that need to use it) but I'd love to hear how you've solved some of the conceptual problems that I've struggled with in trying to make it secure.

Some issues that I've struggled with:

1. withstanding attacks on the file format, some of which are described in this paper: https://www.cs.ox.ac.uk/files/6487/pwvault.pdf. My answer has been the logging system that I talk about in Auditing below but I'd love to hear how you withstand some of these attacks in your system where it looks like you don't even control the file format (is that mysql?)

2. bootstrapping trust. how do I know I haven't been MitM'd, and how can I avoid having to trust the server? My system has a public/private key for every user, so when I share a password with you I'm encrypting it to your public key. The worst possible case then is that I inject my public key claiming to be you because then I get the passwords instead of you. Preventing this while also allowing for public keys to be rotated after a compromise of a client machine is currently stumping me. My current solution is to keep a record of every public key I've seen before, when I see a new one prompt the user to double check it out-of-band (like SSH does), and to just refuse to ever use a new public key for a user that I've seen before. But I bet that users don't actually check this, just like with SSH. And ideally I wouldn't rely on less secure out-of-band communication channels.

3. And speaking of verification, here's how I'm asking users if everything looks right:

    === secrets.vm ===
    common name: secrets.vm
    fingerprint: b957e10c998faa9909cff3ba4ec35485d04708c3ecc7481fe14d7f07bc0229cd
    public key:  c15e697e4807793ef8a9461a7b2c6cf2266d1ec1480a594e83b54e7b75e07702
    public sign: f1db594eb55fe97657c57f2aa01afd1210a46d42d80d5552ac4d548162d4968e
    mnemonic:    AM ROBE KIT OMEN BATE ICY TROY RON WHAT HIP OMIT SUP LID CLAY AVER LEAR CAVE REEL CAN PAM FAN LUND RIFT ACME
    does that look right? [y/n]
AFAICT these word-based (it's rfc1751) or sentence based (as recommended by https://www.usenix.org/conference/usenixsecurity16/technical...) are the best we have but do users actually verify these? How do you help your users prove that your server is really your server before they store secrets in it, in a way that they'll actually verify?

4. Auditing. How can I, as an end user or as an admin of the server, prove that the server hasn't been tampered with to inject rogue keys? Right now I have a sketch for a merkle tree type log system where the server (1) logs every action (2) links every "fact" (a key, a secret, a user) to an entry in the log and (3) signs every log entry along with the log entry before it. Then a user can reconstruct the current state of the database to make sure that it matches. The upside is that it's pretty hard to forge, but the downside is that it's prohibitively expensive to verify: you really have to check the whole thing so you don't want that happening every time you retrieve a secret. And there are some tricky bits like making sure that the log doesn't have orphan entries that are easy to get wrong. Do you have a good solution to the auditing problem?

5. Keeping the crypto itself simple and auditable. I use libnacl for the actual crypto and openssl for TLS. I've found that simply writing down every way the crypto is used (https://github.com/ketralnis/secrets/blob/master/DESIGN.md) that I've found a lot of cases where it was too complicated and likely to be messed up. Finding that out made a big difference to me in how I write it: now I describe it first and then write the code and that has made a huge difference in keeping it simple. I don't see really any documentation on your crypto, how do you know you're doing it right?


For 2,3 the better approach is probably certificates of some form. Ssh supports certificates as well as plain keys (a key with optional meta-data about validity, principals (users,servers etc).

And/or you could use some form of Web of trust model (one could say that a CA system is just a very small, highly trusted wot - typically one key with ultimate trust, that signs all valid certs - as opposed to wot that typically scores a key based on signatures by other (semi-)trusted/known keys).

Unfortunately Ca/wot/certs doesn't solve the problem - but it reduces "verify all the keys" and "rotate all the keys" to verify/rotate the key (eg: the key that signs your user cert - if you have a cert, you have a xhain of trust). It does however bring with it "maintain a CA" - which really is less work than "synchronise all the various public keys" but frequently ends up being hard to decentralise.

The latter is probably solved by realising better is better and having a few people/principals with signing powers (many priest, not just one bishop) and an audit trail, roll-call/revoke - can be a lot safer than ad-hoc trust-on-first-use.


>Ssh supports certificates as well as plain keys (a key with optional meta-data about validity, principals (users,servers etc).

Wouldn't validity and principals be best left to something like LDAP?

I still haven't found anything that fits in the gap between "I only have one machine" and "I'm comfortable setting up Active Directory et-al". I like reading about all these systems but I've only got 5 or so machines under my direct control (although something that integrates with AWS would be nice for when I'm doing development work there)


> Wouldn't validity and principals be best left to something like LDAP?

Yes and no. Even with easier access to caching etc, ldap can still be rather fragile (no (readable) ldap; no login)[1]

In general certificates need to be (proof of) principals (the entity that can prove ownership of the secret key X' corresponding to public key X" is X - where X is a uid (eg: e12e) Or a machine or service (login.example.com).

But if your use case is simple, and some meta-data can be readily attached to the certificate, it's easy to add limits on where key X is valid from (source ip/host), have access to (services) and when (time of day) -- and adding an expiry. If policy is max lifetime of a week except for CA keys, then that greatly simplifies revocation lists.

Generally "trust only this one CA" is simple, and allows moving capability (largely) to certificates. Centralised, on-line catalogs like LDAP make some things simpler.

And there might be a balance: perhaps some basic group info is in the cert - and each service can base policy on that (email login ok 24/7 - certain other services only 8-16 -- although I can't think of a compelling need for the latter right now).

[1] Of course one should check for revocations, but with an automated system to mint certificates, one can trade off with hour/day/week lifetimes. There was a recentish show hn: sharkey:

https://news.ycombinator.com/item?id=12097992


I looked through the project and it is cryptic indeed.

I will try to digest a little bit for people who aren't familiar with Go and by any chance read the project's README

- The examples you see there with commands like `./put ...` `./get ...` are actual Go command line apps found in the `cmd` directory.

What I mean is there are two command line applications which you actually might need to build separately one named `put` the other `get` . What they do, oh! well , never mind it should be crypting stuffs.

After being tasked to do devops for like 6 months. I understand the pain of having more secrets than those of the secret agencies we see on the movies.

Something that turn out to be true most of the times is, secrets should always be secrets. Managing secrets is supposed to be a secret.

I find the Configuration on the README to be full of secrets I mean passwords , API keys e.t.c. And wondering if cryptic is aware of that and how it is going to address this.

With my little understanding about security. I have a feeling that In some cases encryption is mistaken with secure.


> After being tasked to do devops for like 6 months. I understand the pain of having more secrets than those of the secret agencies we see on the movies.

I would highly recommend you read up on this thing called DevOps and what it is about. Start here:https://en.wikipedia.org/wiki/DevOps. You don't just "do devops".




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: