Conversation
zmb3
left a comment
There was a problem hiding this comment.
Haven't tried to run this yet, just did a pass of the code.
zmb3
left a comment
There was a problem hiding this comment.
Looks better, one last thought.
espadolini
left a comment
There was a problem hiding this comment.
Can we unlink the file first, and then overwrite its contents? As it is, anything opening the file while it still exists on disk but is in the process of being overwritten will just get some random garbage.
We can, but this would be an OS conditional logic. I don't think in Windows you can write to files which have been removed from disk. It would be only Linux and MacOS which would allow us to open the handle, remove, and then overwrite. Your recommendation is superior when it can be supported, but since we would have to support both methods is it worth doubling the code? |
|
@jentfoo I don't think we have to double the code, there are already |
|
@AntonAM Perhaps you see something I don't. I don't believe you can simply no-op that single action unless you have other actions which are no-op in the unix case. There is a need for action reordering with regards to the unlink / remove. Unix:
Windows:
If we try to combine these we have a total steps of:
While we could build the combined operation above, and that would technically be less lines. But I feel like it's more confusing than a single conditional which chooses the flow that makes sense for the OS. |
|
Maybe on windows we can rename the file before overwriting it? Especially around key management, I'm wholly convinced that we have code that expects that if a file exists then its contents must be well-formed. |
|
I would be concerned about renaming, that would introduce a new place where the chain of operations may fail and result in a sensitive file being retained on disk. Instead I would lean towards identify and improve any logic that could be impacted by this small overwrite window. |
codingllama
left a comment
There was a problem hiding this comment.
Thanks for yet another security improvement, Jent!
| return &os.PathError{Op: "RemoveAllSecure", Path: path, Err: syscall.EINVAL} // error type matches os.RemoveAll | ||
| } | ||
|
|
||
| if info, err := os.Lstat(path); err != nil { |
There was a problem hiding this comment.
FYI in case you want to try it, but there may be an elegant traversal solution using filepath.WalkDir. No need to change anything though.
|
@jentfoo no, just wanted to highlight this option, I don't insist on the change :) |
|
@AntonAM and @espadolini I updated it with the OS conditional logic, it ended up being cleaner than I had imagined |
There was a problem hiding this comment.
Suggestion: drop separate branches. Builds on the previous suggestions by assuming f can't be nil in this block.
| if runtime.GOOS == "windows" { | |
| // windows can't unlink the file before overwriting | |
| if f != nil { | |
| for i := 0; i < 3; i++ { | |
| _ = overwriteFile(f) | |
| } | |
| } | |
| }() | |
| return trace.ConvertSystemError(os.Remove(filePath)) | |
| } else { | |
| removeErr := os.Remove(filePath) | |
| if f != nil { | |
| for i := 0; i < 3; i++ { | |
| _ = overwriteFile(f) | |
| } | |
| } | |
| return trace.ConvertSystemError(removeErr) | |
| } | |
| } | |
| isWindows := runtime.GOOS == "windows" | |
| var removeErr error | |
| if !isWindows { | |
| // Unlink first on unix systems. | |
| removeErr = os.Remove(filePath) | |
| } | |
| for i := 0; i < 3; i++ { | |
| _ = overwriteFile(f) | |
| } | |
| if isWindows { | |
| removeErr = os.Remove(filePath) | |
| } | |
| return trace.ConvertSystemError(removeErr) | |
| } |
There was a problem hiding this comment.
Mixing the conditional multiple times reduces readability to me, but I don't feel strongly about it
|
Looks good, mostly stylistic comments left. |
zmb3
left a comment
There was a problem hiding this comment.
Approved, but I do think we need to check the sync error (see comment)
There was a problem hiding this comment.
Should we be ignoring an error here?
Writes are buffered in many cases, meaning the error that we are checking (from CopyN) can be nil and the real error would surface here, but we're ignoring it.
There was a problem hiding this comment.
I updated is to that any error (from the copy or sync) will break the overwrite loop, but otherwise the errors are still ignored.
As extra caution, even if an error occurs during the overwrite process, we still want to attempt a removal of sensitive files.
This commit ensures that deleted keyfiles have been overwritten. This has little value on SSD's but can improve the security when the disk is magnetic.
…ible This adds an OS conditional so that if possible the file will be removed and then overwritten using the previous file handle. This will reduces the chance that the file will be witnessed with unexpected contents.
70cee2c to
d1b5d21
Compare
* utils.RemoveSecure: Still attempt a removal after error in overwrite As extra caution, even if an error occurs during the overwrite process, we still want to attempt a removal of sensitive files. * keystore.go: More secure removal of keyfiles This commit ensures that deleted keyfiles have been overwritten. This has little value on SSD's but can improve the security when the disk is magnetic. * Apply PR feedback, notably better testing and early unlinking if possible This adds an OS conditional so that if possible the file will be removed and then overwritten using the previous file handle. This will reduces the chance that the file will be witnessed with unexpected contents.
* utils.RemoveSecure: Still attempt a removal after error in overwrite As extra caution, even if an error occurs during the overwrite process, we still want to attempt a removal of sensitive files. * keystore.go: More secure removal of keyfiles This commit ensures that deleted keyfiles have been overwritten. This has little value on SSD's but can improve the security when the disk is magnetic. * Apply PR feedback, notably better testing and early unlinking if possible This adds an OS conditional so that if possible the file will be removed and then overwritten using the previous file handle. This will reduces the chance that the file will be witnessed with unexpected contents.
* utils.RemoveSecure: Still attempt a removal after error in overwrite As extra caution, even if an error occurs during the overwrite process, we still want to attempt a removal of sensitive files. * keystore.go: More secure removal of keyfiles This commit ensures that deleted keyfiles have been overwritten. This has little value on SSD's but can improve the security when the disk is magnetic. * Apply PR feedback, notably better testing and early unlinking if possible This adds an OS conditional so that if possible the file will be removed and then overwritten using the previous file handle. This will reduces the chance that the file will be witnessed with unexpected contents.
* utils.RemoveSecure: Still attempt a removal after error in overwrite As extra caution, even if an error occurs during the overwrite process, we still want to attempt a removal of sensitive files. * keystore.go: More secure removal of keyfiles This commit ensures that deleted keyfiles have been overwritten. This has little value on SSD's but can improve the security when the disk is magnetic. * Apply PR feedback, notably better testing and early unlinking if possible This adds an OS conditional so that if possible the file will be removed and then overwritten using the previous file handle. This will reduces the chance that the file will be witnessed with unexpected contents.
This PR improves the reliability of secure file removal (particularly under conditions of unexpected errors), and leverages this functionality when removing certificates on disk. This will have little value for SSD's but can be an effective measure when magnetic hard drives are used.
This PR fixes https://github.com/gravitational/teleport-private/issues/670