Skip to content

At rest encryption: TLS keys and wrappers around the etcd wal/snap packages#1598

Merged
aaronlehmann merged 3 commits intomoby:masterfrom
cyli:wal-snapshot-wrapper
Nov 9, 2016
Merged

At rest encryption: TLS keys and wrappers around the etcd wal/snap packages#1598
aaronlehmann merged 3 commits intomoby:masterfrom
cyli:wal-snapshot-wrapper

Conversation

@cyli
Copy link
Contributor

@cyli cyli commented Oct 4, 2016

An implementation of a wrapper that would implement the on-disk snapshot encryption and on-disk WAL encryption as described here: https://docs.google.com/document/d/1YxMH2oIv-mtRcs1djRkm0ndLfiteo0UzBUFKYhLKYkQ/edit?usp=sharing.

This is going to rely on raft.go or storage.go to tell it whether something is encrypted, and with which algorithm. For WALs, such information can be included in the metadata of the file, but no such metadata exists for snapshots. Another option is to base this on the snapshot and WAL directory locations/names, or on a metadata file that lives alongside the raft store.

This wraps every data object in an EncryptedRecord object which specifies the nonce, the algorithm, etc.

  • wrapping code
  • sample encoder/decoder using nacl/secretbox
  • use the wrapping code in all of the current raft storage code
  • migrate existing snapshots and wals to wrapped snapshots and wals
  • encryption key KDF using lock key and node ID <-- we can just take the full key for now, this can be in a separate PR
  • force snapshot whenever the encryption key changes

@codecov-io
Copy link

codecov-io commented Oct 4, 2016

Current coverage is 54.82% (diff: 72.08%)

Merging #1598 into master will decrease coverage by 0.32%

@@             master      #1598   diff @@
==========================================
  Files            97        101     +4   
  Lines         15481      16475   +994   
  Methods           0          0          
  Messages          0          0          
  Branches          0          0          
==========================================
+ Hits           8539       9033   +494   
- Misses         5808       6301   +493   
- Partials       1134       1141     +7   

Sunburst

Powered by Codecov. Last update 1a51ba2...c8f7d60

@aaronlehmann
Copy link
Collaborator

Looks good so far. My preference would be to wait for the whole encryption feature to be ready before merging this, if that works for you. It will let people review it in context and make sure it fits the model.

I think the biggest unresolved question is how to distinguish encrypted and unencrypted WALs/snapshots. For a clean slate approach, I think the best way would be to put encryption metadata this in the protobuf we store at the beginning of each WAL, and the protobuf we serialize into each snapshot file. However, it's not obvious that there's a good backward-compatible way to do this. For example, right now we serialize a RaftMember message into the WAL. We would have to switch to serializing a different message that contains RaftMember as well as encryption info, since clearly that information doesn't belong in RaftMember. But I don't think protobuf encodes the type of a top-level message, so the only way to tell if we are looking at a RaftMember or something else would be to check for the presence of a specific field. If RaftMember later adds anything with the same field number, it becomes ambiguous.

So maybe some other mechanism, like file naming, is better. I guess we should start by asking what exactly information we want to store.

On Oct 4, 2016, at 09:53, Ying Li notifications@github.com wrote:

A simple implementation of a wrapper that would implement the on-disk snapshot encryption and on-disk WAL encryption as described here: https://docs.google.com/document/d/1YxMH2oIv-mtRcs1djRkm0ndLfiteo0UzBUFKYhLKYkQ/edit#.

This is going to rely on raft.go or storage.go to tell it whether something is encrypted, and with which algorithm. For WALs, such information can be included in the metadata of the file, but no such metadata exists for snapshots. Another option is to base this on the snapshot and WAL directory locations/names, or on a metadata file that lives alongside the raft store.

I also have an attempt at a more complex implementation that attempts to wrap some of the encrypted data in a structure that includes such metadata (encryption algorithm) and would be able to read both encrypted and unencrypted data (old and new format data). However, it's currently a bit fragile and we might have to put the digest of the data or some other magic data into the wrapped message.

You can view, comment on, or merge this pull request online at:

#1598

Commit Summary

Implement an encryption wrapper around WALs
Implement snapshot encoding and decoding
Add a no-op implementation of a decoder and encoder
Convert the raft store over to use the wrapped WAL and Snapshot writers
File Changes

A manager/state/raft/encryption/common_test.go (28)
A manager/state/raft/encryption/interfaces.go (28)
A manager/state/raft/encryption/snapwrap.go (94)
A manager/state/raft/encryption/snapwrap_test.go (167)
A manager/state/raft/encryption/walwrap.go (106)
A manager/state/raft/encryption/walwrap_test.go (244)
M manager/state/raft/raft.go (7)
M manager/state/raft/storage.go (8)
Patch Links:

https://github.com/docker/swarmkit/pull/1598.patch
https://github.com/docker/swarmkit/pull/1598.diff

You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or mute the thread.

@stevvooe
Copy link
Contributor

stevvooe commented Oct 4, 2016

Is there a way to simply encrypt the stream, rather than having the encryption work at the message-level? This approach intertwines the encryption format with the message structure, which is a level of coupling that isn't really necessary. We can see this in the fact that term and index aren't actually encrypted.

Ideally, we should be able to hand in an io.Writer or cipher.StreamWriter and have that encrypt everything behind the scenes. This way, if we change the format, we won't have to also consider the encryption approach.

@cyli
Copy link
Contributor Author

cyli commented Oct 5, 2016

My preference would be to wait for the whole encryption feature to be ready before merging this, if that works for you. It will let people review it in context and make sure it fits the model.

@aaronlehmann Sure, no worries - was just trying to keep the PRs smaller. :)

So maybe some other mechanism, like file naming, is better. I guess we should start by asking what exactly information we want to store.

I was originally thinking the encryption algorithm, the hash if we're not going to do GCM and hence need to provide our own digest. We could possibly the ID of the key, if we store the encryption key as a secret. Possibly the IV or nonce, if one is used.

Is there a way to simply encrypt the stream, rather than having the encryption work at the message-level? This approach intertwines the encryption format with the message structure, which is a level of coupling that isn't really necessary. We can see this in the fact that term and index aren't actually encrypted.

@stevvooe I agree it's not great to tie things to the Record structure, although I was thinking the index and the term would give us a non-repeating IV (which would have to be stored somewhere unencrypted anyway, maybe prefixed to the ciphertext, or generated with a shared nonce).

The WAL records and snapshots are serialized inside the wal and snap packages before writing to disk (none of the attributes of the WAL and Snapshotter types are exported - I couldn't find a good place to inject anything, but I may have just missed it), so I think we'd basically have to maintain a fork of at least those packages (or get this upstream) in order to do so as you suggest. They'd have to accept the writer or some cipher.*, and also write in the encryption format of the file somewhere (filename or in the metadata object).

Another approach @NathanMcCauley suggested is that we can encrypt above the raft layer.

So we can do that at the Store before it gets into the snapshot and logs, perhaps here: https://github.com/docker/swarmkit/blob/master/manager/state/store/memory.go#L663 and here https://github.com/docker/swarmkit/blob/master/manager/state/store/memory.go#L441? Or during per-object serialization.

We can pass a cipher.Stream or something to MemoryStore. This would mean we'd have to change the api.StoreAction and api.Snapshot protocols... But maybe this way #363 can be addressed at the same time.

This also means that the snapshots and raft logs will also be sent encrypted (beyond just TLS) between peers, which isn't a feature we need but not necessarily a bad thing. Each node would have to be bootstrapped with the encryption key before being able read the Store part of snapshots, or apply raft log entries involving the Store, but we would have needed to unlock the nodes on restart at any rate, with the previous design.

The problem is that new encryption key distribution to all the nodes using the Store becomes more complex, since we don't necessarily know that all the nodes (just a majority) have gotten the key (please correct me if my assumption is incorrect - I think we only apply an update to the Store when the proposed change has been committed, which means the log has successfully been replicated to a majority of the nodes). So if we need to catch up a node that's really far behind and needs a snapshot, that snapshot may be encrypted with a new key. This isn't great because it would require unlocking after a particularly egregious network partition during a key rotation, as opposed to just on restart.

Possibly this can be dealt with using the 3 key network rotation thing, and just let users rotate key encryption keys. Or maybe we can use the manager TLS keys to encrypt the Store encryption key (and encrypt the TLS certificates themselves).

I think that wrapping the wal and snapshot packages is the simplest, but I agree not the cleanest. I'm not sure which of the above two options would be better - each have their tradeoffs too. Any advice would be appreciated. :)

@aaronlehmann
Copy link
Collaborator

I don't really like the idea of encrypting raft log entries before they are distributed.

  • We should rely on TLS for that
  • It means all nodes need to share the same key for at-rest encryption
  • It makes key distribution way more complex

Given the structure of the wal package, I think wrapping it like this is the best approach, as discussed previously. Otherwise we would have to maintain a fork.

On Oct 5, 2016, at 07:42, Ying Li notifications@github.com wrote:

My preference would be to wait for the whole encryption feature to be ready before merging this, if that works for you. It will let people review it in context and make sure it fits the model.

@aaronlehmann Sure, no worries - was just trying to keep the PRs smaller. :)

So maybe some other mechanism, like file naming, is better. I guess we should start by asking what exactly information we want to store.

I was originally thinking the encryption algorithm, the hash if we're not going to do GCM and hence need to provide our own digest. We could possibly the ID of the key, if we store the encryption key as a secret.

Is there a way to simply encrypt the stream, rather than having the encryption work at the message-level? This approach intertwines the encryption format with the message structure, which is a level of coupling that isn't really necessary. We can see this in the fact that term and index aren't actually encrypted.

@stevvooe I agree it's not great to tie things to the Record structure, although I was thinking the index and the term would give us a non-repeating IV (which would have to be stored somewhere unencrypted anyway, maybe prefixed to the ciphertext, or generated with a shared nonce).

The WAL records and snapshots are serialized inside the wal and snap packages before writing to disk (none of the attributes of the WAL and Snapshotter types are exported - I couldn't find a good place to inject anything, but I may have just missed it), so I think we'd basically have to maintain a fork of at least those packages (or get this upstream) in order to do so as you suggest. They'd have to accept the writer or some cipher.*, and also write in the encryption format of the file somewhere (filename or in the metadata object).

Another approach @NathanMcCauley suggested is that we can encrypt above the raft layer.

So we can do that at the Store before it gets into the snapshot and logs, perhaps here: https://github.com/docker/swarmkit/blob/master/manager/state/store/memory.go#L663 and here https://github.com/docker/swarmkit/blob/master/manager/state/store/memory.go#L441? Or during per-object serialization.

We can pass a cipher.Stream or something to MemoryStore. This would mean we'd have to change the api.StoreAction and api.Snapshot protocols... But maybe this way #363 can be addressed at the same time.

This also means that the snapshots and raft logs will also be sent encrypted (beyond just TLS) between peers, which is a feature we were looking for but not necessarily a bad thing. Each node would have to be bootstrapped with the encryption key before being able read the Store part of snapshots, or apply raft log entries involving the Store, but we would have needed to unlock the nodes on restart at any rate, with the previous design.

The problem is that encryption key distribution to all the nodes using the Store becomes more complex, since we don't necessarily know that all the nodes (just a majority) have gotten the key (please correct me if my assumption is incorrect - I think we only apply an update to the Store when the proposed change has been committed, which means the log has successfully been replicated to a majority of the nodes). So if we need to catch up a node that's really far behind and needs a snapshot, that snapshot may be encrypted with a new key. This isn't great because it would require unlocking after a particularly egregious network partition during a key rotation, as opposed to just on restart.

Possibly this can be dealt with using the 3 key network rotation thing, and just let users rotate key encryption keys. Or maybe we can use the manager TLS keys to encrypt the Store encryption key (and encrypt the TLS certificates themselves).

I think that wrapping the wal and snapshot packages is the simplest, but I agree not the cleanest. I'm not sure which of the above two options would be better - each have their tradeoffs too.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.


func (m *meowCoder) Decode(index, term uint64, orig []byte) ([]byte, error) {
if !bytes.HasSuffix(orig, []byte("🐱")) {
return nil, fmt.Errorf("not meowcoded")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clever ;)

@diogomonica
Copy link
Contributor

diogomonica commented Oct 5, 2016

@aaronlehmann

  • The fact that we use TLS to provide these guarantees doesn't mean that we shouldn't do data encryption too.
  • Yes, it does mean that everyone uses the same key
  • No, the key distribution mechanism is still manual key distribution on manager startup (same exact thing).

@cyli one possibility is an out-of-raft key distribution mechanism, such as an API that when called with a manager certificate returns the current key.

I still agree that the best way forward is the current path (wrapper around the WAL), but the best solution is we encrypt above raft.

@cyli
Copy link
Contributor Author

cyli commented Oct 5, 2016

Other random thoughts that may affect the decision:

  1. We may need some some sort of out-of-raft mechanism to sync all the managers to do a CA rotation anyway, and a similar mechanism can be used here for out-of-band encryption key sync (re: @diogomonica's comment above)
  2. Encrypting the individual Store object serializations may mean that if we needed to, we can keep a cache on disk as well as in memory if we started keeping larger histories (like caching individual entries by IDs, as opposed to a snapshot which is intended to be a backup that gets loaded into memory)
  3. We can also by default use the join token as a KDF to generate the initial encryption key (users can specify a different key or passphrase for the KDF if needed)
  4. The wrapper method does tie us to etcd's specific serialization format, and we may have some ugliness to deal with if they change the format (although I don't think they can move away from an object that contains a byte array)
  5. Even if we encrypt below raft, and we put the encryption key in side the store and use raft to synchronize it across nodes, a manager may fall behind and not get the latest encryption key. This only means that it needs to be unlocked with a previous encryption key though, and doesn't prevent it from catching up if it's still online.
  6. I don't think picking one method now necessarily precludes us from doing the other in the future.

@lvh
Copy link

lvh commented Oct 6, 2016

I agree with @diogomonica that encrypting Raft entries is the way to go. I assumed that's how it would work from the start. This has a few advantages, including e.g. that you don't really care if it's Raft anymore.

Do you actually need key rotation? If so, why? I can think of a few reasons, which all have different answers. If your primitive won't let you encrypt large amounts of data, derive a new key, or pick a better primitive, or both. In the case of compromise, you should be nuking the secret store from orbit anyway and rotate all of the secrets stored inside it. (Plus, that's ideally a rare case, so I'm not sure if I see the value for optimizing it.) If you're worried about algorithmic agility: a) pick a strong primitive and don't mess it up (I'm volunteering to help with that) b) that should also be a rare event, so just moving all the data over should still be an acceptable solution.

I'm not saying that key rotation is intrinsically bad or that you can't have it; just that it doesn't come free, and it's not obvious to me why you'd want it.

New nodes already need some kind of secret to be able to join the cluster (the join token), and once you get a 256 bit secret you should be set for the lifetime of the cluster. You might not be able to bootstrap the secret from he join token (those apparently are easy to regenerate and the key should be constant for a cluster), but whatever the thing is that issues certificates could just tell you about the key as well.

@cyli
Copy link
Contributor Author

cyli commented Oct 7, 2016

I'm not saying that key rotation is intrinsically bad or that you can't have it; just that it doesn't come free, and it's not obvious to me why you'd want it.

Not having to re-bootstrap the entire swarm cluster and thus losing all stored data (and rotating out the individual secrets for instance) would be good. But as you and @diogomonica mention there are other solutions for the key distribution.

@lvh
Copy link

lvh commented Oct 7, 2016

@cyli I'm not sure I follow. Wouldn't you be able to do that regardless just by writing new entries into the Raft log? That doesn't seem like it'd require changing the encryption key for that cluster.

@cyli
Copy link
Contributor Author

cyli commented Oct 7, 2016

@lvh Er, sorry, I mean if the encryption key is ever compromised, not having to have to create an entirely new swarm cluster would be good (rotate encryption key and rotate your secrets individually)

@cyli cyli changed the title Wrapper around the etcd wal and snap packages for encryption WIP: Wrapper around the etcd wal and snap packages for encryption Oct 11, 2016
@cyli cyli force-pushed the wal-snapshot-wrapper branch 10 times, most recently from 6fac38a to 767c8f7 Compare October 14, 2016 03:46
@cyli cyli changed the title WIP: Wrapper around the etcd wal and snap packages for encryption Wrapper+Encryption around the etcd wal and snap packages Oct 14, 2016
@cyli
Copy link
Contributor Author

cyli commented Oct 14, 2016

Might be ready for another look. This does the encryption part of the wrapping, and handles forcing a new snapshot if the key is rotated.

Couple of caveats:

  1. It is currently using a shared key across the cluster to encrypt, and expects a 32-byte key from the user currently. This can easily be changed by using scrypt to generate an encryption key per node (so the user can provide a password instead of a full encryption key), possibly with the node ID as the salt so the encryption key is different per node.
  2. Not sure what should happen if:
  • The user migrates from an existing cluster - there is no encryption key, and currently it's just encrypting with an empty key. Should we require a key if a user upgrades?

  • A node joins an existing cluster. If no lock key is provided, then it will just wait for an update from the rest of the cluster to get the key... unfortunately, that raft update containing the key will be written unencrypted to a WAL. Once it detects a key change, a new snapshot will be written and everything from then on will be encrypted, however:

    • that encryption key remains unencrypted (encrypted with a blank key) on disk until the WAL file is rotated and then GC'ed
    • might remain anyway if there is an issue with GC
    • will remain unencrypted (well encrypted with an empty key for now) on disk if the node goes down before finishing the snapshot

    We can (1) require that joining a cluster also takes a lock key, (2) refuse to write any WALs to disk unless there is a non-empty encryption key, or (3) write a WAL to disk with a randomly generated throwaway key which will behave the same as (2), if the node goes down the data will be unreadable.

This also may also be a little tricky because order of raft operations has to be right (I tested as best I could - I've probably missed something obvious). Going to also try out the above-raft implementation too to see if it's simpler, especially since it turns out this also has some key-bootstrap issues, and we can see which one we like.

cc @aaronlehmann @stevvooe

@aaronlehmann
Copy link
Collaborator

I'll defer to you and Diogo on KDF vs. raw key.

I'm not yet convinced that a shared key is a good idea. Storing the key in raft causes a lot of problems. I think it would be better to let each node use its own key. If the admin wants to use the same key everywhere, they're free to do that - or they can generate a separate key for every node, which potentially improves security. Automatically distributing a shared key definitely worries me.

I also get concerned when I see discussion of possibilities like "require that joining a cluster also takes a lock key" and "refuse to write any WALs to disk unless there is a non-empty encryption key". I think it's really important that this feature not cause extra hurdles for normal users. I expect very, very few people will make meaningful use of the encryption-at-rest feature, since it will be a big operational hassle to unlock the encrypted files after a restart. I'm okay with adding the feature as long as it's opt-in and doesn't cause problems for normal users. But I see this moving in the direction of becoming more tightly integrated and user-impacting and TBH I don't really like that.

api/specs.proto Outdated
// TaskDefaults specifies the default values to use for task creation.
TaskDefaults task_defaults = 7 [(gogoproto.nullable) = false];

// LockKey is the encryption key for the raft data
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now, as I understand, this is limited to raft. Do we have plans on opening this up to "at rest" data? Would it be more accurate to say this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, do we need to store the new and old key here in case the migration is interrupted?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now, as I understand, this is limited to raft. Do we have plans on opening this up to "at rest" data? Would it be more accurate to say this?

Sorry, yes, this is intended for everything at rest - I just hadn't implemented that part yet, and though I was going to do that in another PR, so I was intended on updating it then when the feature was in. After some discussion with @aaronlehmann this is PR is going to implement everything nuts to bolts, so I'll update the comment. :)

Also, do we need to store the new and old key here in case the migration is interrupted?

I'm not sure the old key inside the ClusterSpec itself is necessary, since we don't need to be able to read old data except on starting up. And on starting up, we won't be able to read data in here anyway. :) We just need the new key in order to write new data.

I think we are going to not use a shared key though, so I guess it won't be distributed via raft anymore. If we have a data encryption key + key encryption key, then yes if the data encryption key is rotated we'll have to keep the old one around.

func (l *lockKeyRotator) MaybeUpdateKey(newKey []byte) {
l.mu.Lock()
defer l.mu.Unlock()
if !bytes.Equal(l.currentKey, newKey) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if the key is updated while already in the midst of a rotation?

Copy link
Contributor Author

@cyli cyli Oct 14, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rotation should happen as a callback inside the DoRotation function, which grabs a write lock. I think this should prevent the key from actually being updated whilst in the middle of a rotation? See https://github.com/docker/swarmkit/pull/1598/files#diff-fcccb5a38e5148a94f6ecfa031233c31R261. I didn't lock the entire doSnapshot call, because it only matters when we actually save a snapshot to disk.

So what will happen if you rotate and rotate again is (at least this is what I was going for...):

  1. MaybeUpdateKey grabs a lock. Currently no other rotation happening, so it updates the key.
  2. The logic that calls MaybeUpdateKey sees if there's a snapshot in progress. Let's say there isn't for now, so it calls doSnapshot
  3. Before doSnapshot actually saves a snapshot to disk, it checks if it needs to rote, and if so, actually does rotate out the keys by changing the encoder in side the wrapped snapshotter and WAL writer. It grabs a lock while doing so.
  4. saveSnapshot now starts writing stuff to disk.
  5. Let's say another cluster update comes in. MaybeUpdateKey is triggered again. If it happens in the middle of the encoder rotation being done in saveSnapshot, it will wait until that's done. Then it updates the key.
  6. Because a snapshot is already in progress, nothing happens - no new snapshot is saved. The key has been rotated, but it's not being used because the encoders haven't been rotated.
  7. When the snapshot is done, the snapshotInProgress channel is written to, then set to nil. Then it checks to see if a rotation needs to be done. If so, it triggers another doSnapshot.
  8. Which calls saveSnapshot, which will rotate the key as necessary, etc.

So if you rotate a key over and over, snapshots will be written over and over to disk, but the cached keys in keyRotator can change independently of the actual key used for encryption. The keyRotator is more of an indication of whether the key currently used for encryption needs to change.

}

// the wal/snap directories in decreasing order of preference/version
var versionedWalSnapDirs = []walSnapDirs{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

versionedWALSnapDirs

return err
}

if err := e.snapshotter.SaveSnap(snapshot); err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to hold the lock and block key rotation while the snapshot is actually saved? Could we do something like:

snapshotter := e.snapshotter
e.encoderMu.RUnlock()
if err := snapshotter.SaveSnap(snapshot); err != nil {

// ReadRepairWAL opens a WAL for reading, and attempts to read it. If we can't read it, attempts to repair
// and read again.
func ReadRepairWAL(ctx context.Context, walDir string, walsnap walpb.Snapshot, factory WALFactory) (
WAL, WALData, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't break the line here

@cyli cyli force-pushed the wal-snapshot-wrapper branch 7 times, most recently from 84f0f79 to 7815619 Compare November 8, 2016 10:15
node/node.go Outdated
close(n.certificateRequested)
if securityConfig == nil {
switch {
case n.config.JoinAddr == "":
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I'd format this as an if/else

err := os.MkdirAll(filepath.Dir(k.paths.Key), 0755)
if err != nil {
// current assumption is that the cert and key will be in the same directory - we
// create a temporary directory
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't look like a temporary directory. Is the comment wrong, or is the path wrong?

cluster = store.GetCluster(tx, cluster.ID)
})

time.Sleep(250 * time.Millisecond) // server needs to get update event
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we poll for the correct key instead? (sorry)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea 👍

@cyli cyli force-pushed the wal-snapshot-wrapper branch 4 times, most recently from e3394c7 to bca338c Compare November 9, 2016 00:30
@aaronlehmann
Copy link
Collaborator

LGTM

@cyli cyli force-pushed the wal-snapshot-wrapper branch 2 times, most recently from 18c71ce to c8f7d60 Compare November 9, 2016 21:05
cyli added 3 commits November 9, 2016 14:01
1.  There is a single object that knows how to read/write TLS keys and certs.  This object knows how
    to encrypt/decrypt the TLS key, and preserve any headers stored on the key as specified by a
    PEMKeyHeaders interface.
2.  No longer write the Root CA key to disk - it is stored inside raft only.

Refactor the node TLS loading/bootstrapping as well to live in a single function.

Signed-off-by: cyli <ying.li@docker.com>
…y so that so that

TLS certificates are never written to disk after they are downloaded from the swarm CA.

Also, update the cluster spec and object so that a user can enable and disable autolocking
managers. If autolocking is enabled, a shared key will be used by all the managers to
encrypt TLS keys and raft DEKs.

Also, provide the control API and swarmd/swarmct commands to enable/disable autolocking,
and rotate lock keys.

Signed-off-by: cyli <ying.li@docker.com>
… snapshot into a

storage package that knows how to do encryption and rotate encryption keys.

Handle raft DEK encryption rotation in the raft node and in the manager.

Ensure that when a node starts up, it uses a PEMKeyHeader interface that
knows how to handle raft DEKs.

Signed-off-by: cyli <ying.li@docker.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants