Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[tor] Add ability to encrypt the Tor private key on disk #4458

Closed

Conversation

gkrizek
Copy link
Contributor

@gkrizek gkrizek commented Jul 12, 2020

It's possible that a user might not want the Tor private key to sit on the disk in plaintext (it is a private key after all). So this commit adds a new flag to encrypt the Tor private key on disk using the wallet's seed. When the --tor.encryptkey flag is used, LND will still write the Tor key to the same file, however it will now be encrypted instead of plaintext. This essentially uses the same method to encrypt the Tor private key as is used to encrypt the Static Channel Backup file.

Summary of Changes:

  • Moves the crypto.go file from chanbackup into its own package called lnencrypt.

This was needed because we can't directly reference the encryptPayloadToWriter and decryptPayloadFromReader functions from the chanbackup package due to circular dependency issues. Also, I think it's very likely that future enhancements will also want to use these same functions. Thus I've moved them to their own package.

  • Creates a flag called --tor.encryptkey which encrypts the Tor private key.

When this is set it encrypts the private key and writes the encrypted blob to the same file on disk. This allows users to still keep the same functionality and flows as before (like refreshing a hidden service), but adds protection when running in untrusted environments.

  • Add necessary tests and documentation

@wpaulino wpaulino added enhancement Improvements to existing features / behaviour security General label for issues/PRs related to the security of the software tor v0.12 labels Jul 13, 2020
@gkrizek
Copy link
Contributor Author

gkrizek commented Jul 15, 2020

Discussion question: In this implementation, if LND is started with --tor.encryptkey it encrypts the key on disk. However, if LND was later started without the --tor.encryptkey it would write the key to disk in plaintext (because it wasn't instructed to encrypt it). Is this the best behavior? Should we always save the key in an encrypted format if the flag is passed and never let it be stored in plaintext?

@gkrizek gkrizek force-pushed the encrypt-tor-privatekey branch from 8dc7725 to 7913317 Compare July 21, 2020 14:47
@Roasbeef Roasbeef requested review from Crypt-iQ and removed request for Roasbeef and cfromknecht July 22, 2020 02:55
@Roasbeef Roasbeef added this to the 0.12.0 milestone Jul 22, 2020
Copy link
Contributor

@wpaulino wpaulino left a comment

Choose a reason for hiding this comment

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

However, if LND was later started without the --tor.encryptkey it would write the key to disk in plaintext (because it wasn't instructed to encrypt it). Is this the best behavior? Should we always save the key in an encrypted format if the flag is passed and never let it be stored in plaintext?

In that case, a user-friendly error can be returned to prevent startup noting that the private key is encrypted and the flag should be toggled to continue. In the future, we may want to consider toggling the flag by default, but that'd be a breaking change.

server.go Outdated Show resolved Hide resolved
tor/add_onion.go Outdated Show resolved Hide resolved
tor/add_onion.go Outdated Show resolved Hide resolved
watchtower/standalone.go Outdated Show resolved Hide resolved
@gkrizek gkrizek force-pushed the encrypt-tor-privatekey branch from 7913317 to a17c609 Compare August 11, 2020 03:14
@gkrizek
Copy link
Contributor Author

gkrizek commented Aug 11, 2020

Thanks for the review, @wpaulino! I've addressed your comments, PTAL.

I also added a condition if the Tor private key is encrypted but LND doesn't have --tor.encryptkey enabled. It will now log a detailed error message and exit.

tor/add_onion.go Outdated Show resolved Hide resolved
@gkrizek gkrizek force-pushed the encrypt-tor-privatekey branch from a17c609 to bb3365e Compare August 11, 2020 20:29
Copy link
Contributor

@wpaulino wpaulino left a comment

Choose a reason for hiding this comment

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

Some final minor nits and we're good to go!

watchtower/standalone.go Outdated Show resolved Hide resolved
watchtower/standalone.go Outdated Show resolved Hide resolved
@gkrizek gkrizek force-pushed the encrypt-tor-privatekey branch from bb3365e to dde6f61 Compare August 14, 2020 00:27
@wpaulino
Copy link
Contributor

@gkrizek the linter reported some failures. Could you address those?

@gkrizek gkrizek force-pushed the encrypt-tor-privatekey branch from dde6f61 to 1e239ec Compare August 15, 2020 01:10
@gkrizek
Copy link
Contributor Author

gkrizek commented Aug 15, 2020

@wpaulino Addressed. make lint and make check pass locally. It was mainly complaining about some of the test code I did which I refactored to please the linter so that may be worth a new check. Thanks!

@gkrizek gkrizek force-pushed the encrypt-tor-privatekey branch from 1e239ec to 7d3c553 Compare August 15, 2020 01:14
@Crypt-iQ
Copy link
Collaborator

Reviewing now

@wpaulino wpaulino removed their request for review August 16, 2021 17:23
@Roasbeef Roasbeef modified the milestones: v0.14.0, v0.15.0 Oct 19, 2021
@gkrizek gkrizek force-pushed the encrypt-tor-privatekey branch from 9f777c3 to 430c1fc Compare November 3, 2021 04:21
@gkrizek
Copy link
Contributor Author

gkrizek commented Dec 20, 2021

ping. I think this would still be valuable.

@Crypt-iQ
Copy link
Collaborator

Crypt-iQ commented Jan 3, 2022

In some cases I could see this being valuable, like if there happened to be a remote file-system leak vulnerability in lnd. But IMO our code isn't complex enough to have one of those bugs. That leaves us in the realm of access to the machine that lnd is running on. If somebody has access to the machine, it is already game over as they could dump the memory of the running process if they have privileges. If they don't, they could perform any one of the well-documented side-channel attacks that exist. For those reasons I'm not sure it makes sense to give users a false sense of security at the cost of increased code complexity.

Copy link
Collaborator

@guggero guggero left a comment

Choose a reason for hiding this comment

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

I think this is a nice security feature and makes sense to add.

Did a high-level pass and things look pretty good functionality wise (didn't run any manual tests yet, will do so in the second pass). But there are quite a few style related comments, also from previous reviewers. I think in general the commits should be smaller too.
Will do a second pass once the commit structure is cleaned up a bit.

chanbackup/backupfile_test.go Show resolved Hide resolved
keychain/derivation.go Show resolved Hide resolved
keychain/derivation.go Show resolved Hide resolved
lnencrypt/crypto.go Outdated Show resolved Hide resolved
lnencrypt/crypto_test.go Outdated Show resolved Hide resolved
lntest/mock/secretkeyring.go Outdated Show resolved Hide resolved
lnencrypt/tor.go Outdated Show resolved Hide resolved
tor/cmd_onion.go Outdated Show resolved Hide resolved
tor/onionfile/onion_file.go Show resolved Hide resolved
gkrizek and others added 3 commits February 2, 2022 18:12
…its own package called lnencrypt

The functions inside of the crypto.go file in chanbackup (like EncryptPayloadToWriter and DecryptPayloadFromReader) can be used by a lot of things outside of just the chanbackup package. We can't just reference them directly from the chanbackup package because it's likely that it would generate circular dependencies. Therefore we need to move these functions into their own package to be referenced by chanbackup and whatever new functionality that needs them
@gkrizek gkrizek force-pushed the encrypt-tor-privatekey branch from 430c1fc to 8129b6a Compare February 3, 2022 03:43
gkrizek and others added 3 commits February 3, 2022 19:59
…ity to encrypt the Tor private key on disk

It's possible that a user might not want the Tor private key to sit on the disk in plaintext (it is a private key after all). So this commit adds a new flag to encrypt the Tor private key on disk using the wallet's seed. When the --tor.encryptkey flag is used, LND will still write the Tor key to the same file, however it will now be encrypted intead of plaintext. This essentially uses the same method to encrypt the Tor private key as is used to encrypt the Static Channel Backup file.
@gkrizek gkrizek force-pushed the encrypt-tor-privatekey branch from 8129b6a to a9fcaea Compare February 4, 2022 05:02
@gkrizek gkrizek requested a review from guggero February 4, 2022 05:03
@gkrizek
Copy link
Contributor Author

gkrizek commented Feb 4, 2022

@guggero, @orbitalturtle has addressed all your comments. Please take another look. Thanks!

Copy link
Collaborator

@guggero guggero left a comment

Choose a reason for hiding this comment

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

Looks good, thanks for the update. We're definitely getting closer.

@@ -188,7 +190,7 @@ func assertMultiEqual(t *testing.T, a, b *Multi) {
func TestExtractMulti(t *testing.T) {
t.Parallel()

keyRing := &mockKeyRing{}
keyRing := &lnencrypt.MockKeyRing{}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can't we move to the mock.MockKeyRing here directly? Instead of creating a new one and then moving away from that in a later commit?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah yes so I think I was attempting to split out the mock key change into a new PR as requested here #4458 (comment)

But perhaps it'd be better to resquash it into one commit? Thoughts?


// NewOnionFile creates a file-based implementation of the OnionStore interface
// to store an onion service's private key.
func NewOnionFile(privateKeyPath string, privateKeyPerm os.FileMode,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Now we still have two OnionFile structs. Can't this be integrated into the existing one? It seems we just add two new parameters and some additional logic. Or is the idea to remove the tor.OnionFile since it's not used anymore? I think the diff could be way smaller if we just integrated this new code into the existing struct.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes deleted original onionfile in the Tor package. Moved the onionfile to a new package based on the convo in here: #4458 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

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

Ooooohhhh, I see now. Yeah I never deleted the OnionFile data in cmd_onion. Will fix that !

@Roasbeef Roasbeef removed this from the v0.15.0 milestone Apr 12, 2022
@lightninglabs-deploy
Copy link

@Crypt-iQ: review reminder
@gkrizek, remember to re-request review from reviewers when ready

@guggero
Copy link
Collaborator

guggero commented May 5, 2022

Replaced by #6500.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Improvements to existing features / behaviour P2 should be fixed if one has time security General label for issues/PRs related to the security of the software tor
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants