-
Notifications
You must be signed in to change notification settings - Fork 357
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
feat: Allow master configuration for ssh key crypto system #10072
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #10072 +/- ##
==========================================
+ Coverage 54.42% 54.45% +0.02%
==========================================
Files 1262 1262
Lines 158886 158910 +24
Branches 3630 3630
==========================================
+ Hits 86472 86527 +55
+ Misses 72280 72249 -31
Partials 134 134
Flags with carried forward coverage won't be shown. Click here to find out more.
|
✅ Deploy Preview for determined-ui canceled.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there's a typo in PR / commit template: EC25519
instead of ED25519
. everything looks ok in the code though.
|
||
func generateECDSAKey() (PrivateAndPublicKeys, error) { | ||
var generatedKeys PrivateAndPublicKeys | ||
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just as a note: I don't think there were any requests to make the curve size configurable, and P256 is the NIST recommendation, so this is fine. But one day we may have to update it or make configurable.
- Master Configuration: Add support for crypto system configuration for ssh connection. ``security | ||
-> crypto_system`` now accepts ``RSA``, ``ECDSA`` or ``ED25519``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking through the release notes, we usually use period instead of ->
, i.e. security.crypto_system
.
- Master Configuration: Add support for crypto system configuration for ssh connection. ``security | |
-> crypto_system`` now accepts ``RSA``, ``ECDSA`` or ``ED25519``. | |
- Master Configuration: Add support for crypto system configuration for ssh connection. ``security.crypto_system`` now accepts ``RSA``, ``ECDSA`` or ``ED25519``. |
master/internal/config/config.go
Outdated
@@ -108,7 +117,8 @@ func DefaultConfig() *Config { | |||
Group: "root", | |||
}, | |||
SSH: SSHConfig{ | |||
RsaKeySize: 1024, | |||
RsaKeySize: 1024, | |||
CryptoSystem: RSACryptoSystem, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was under impression we wanted to switch to ECDSA by default, because it's faster and compliant with FIPS140-2 requirements. I'd strongly suggest we do the switch, while leaving an option for the users to revert back to RSA if they really want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if @mackrorysd or @rb-determined-ai want to say anything about this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is faster than reasonably-sized RSA keys. I never liked the 1024 key size choice when we made it years ago, because key-per-task took too long if you used 2048 keys. The argument I made was "let's not do one key per task then" And the argument made back to me was "1024 is fine if the tasks are not long lived" and I lost.
Certainly I don't think 1024 rsa keys is a good default by any measure.
I personally would default to ed25519 keys, but I think Ilia is right about what most customers would prefer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait, actually, I do vote ed25519 because that's what ssh-keygen
defaults to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know too much about ssh keys, but it looks like newer algorithms are better in terms of the balance of security and speed, so I put ed25519
down as default for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ed25519 is not in FIPS140, so the category of users who cares about that will have to be aware of it and change the config. ECDSA is still plenty fast and fit most requirements, which feels like the best fit for the default option.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't have a strong opinion here myself, but to repeat some information added by @dannysauer:
FWIW: FIPS 186-5, Digital Signature Standard includes ED25519 as of its official publication date Feb last year.
https://csrc.nist.gov/pubs/fips/186-5/final
This is helpful, because ECDSA depends on having a good random number generator, which is not always the case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would side with ED25519 here the more I've read about it. It seems to be the technically superior solution, it is getting accepted in other federal standard, etc.
master/internal/config/config.go
Outdated
@@ -452,7 +462,8 @@ type SecurityConfig struct { | |||
|
|||
// SSHConfig is the configuration setting for SSH. | |||
type SSHConfig struct { | |||
RsaKeySize int `json:"rsa_key_size"` | |||
RsaKeySize int `json:"rsa_key_size"` | |||
CryptoSystem string `json:"crypto_system"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
crypto_system
seems like an unexpected phrase for what it means. How about key_type
?
For example, man ssh-keygen says
:
The type of key to be generated is specified with the -t option. If invoked without any arguments, ssh-keygen will generate an Ed25519 key.
master/internal/config/config.go
Outdated
// RSACryptoSystem uses RSA. | ||
RSACryptoSystem = "RSA" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace 'CryptoSystem' with 'KeyType' or something.
- Master Configuration: Add support for crypto system configuration for ssh connection. | ||
``security.key_type`` now accepts ``RSA``, ``ECDSA`` or ``ED25519``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth mentioning the transition to ED25519 keys by default, and I'd also mention that ED25519 keys are faster and more secure than the old default, as well as being the default key type for ssh-keygen
.
Mentioning that we're using more secure keys makes us look good, and mentioning that we're adopting the same default as ssh-keygen helps to reduce support burden by making the change sound like obvious rather than arbitrary.
master/pkg/ssh/ssh_test.go
Outdated
} | ||
|
||
func TestSSHKeyGenerate(t *testing.T) { | ||
keys, err := GenerateKey(config.SSHConfig{KeyType: config.RSACryptoSystem, RsaKeySize: 1024}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: for the unit test, can you use a keysize of 512?
It's absurdly small for security purposes, but it should exercise the codepaths just fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
stamped from infra
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm, only have non-blocking feedback (i.e., i'd appreciate it being addressed but don't explicitly expect to re-review)
@@ -118,7 +118,7 @@ func setupAPITest(t *testing.T, pgdb *db.PgDB, | |||
TaskContainerDefaults: model.TaskContainerDefaultsConfig{}, | |||
ResourceConfig: *config.DefaultResourceConfig(), | |||
}, | |||
taskSpec: &tasks.TaskSpec{SSHRsaSize: 1024}, | |||
taskSpec: &tasks.TaskSpec{SSHConfig: config.SSHConfig{RsaKeySize: 1024, KeyType: "RSA"}}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as a follow up: can/should we default to ed25519?
master/internal/config/config.go
Outdated
if t.KeyType != RSAKeyType && t.KeyType != ECDSAKeyType && t.KeyType != ED25519KeyType { | ||
errs = append(errs, errors.New("Crypto system must be one of 'RSA', 'ECDSA' or 'ED25519'")) | ||
} | ||
if t.KeyType != RSAKeyType { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we flip this and put this rsa specific stuff in an if t.KeyType == RSAKeyType
block? written like this, it's easy for someone to add a check and the bottom of this func and not realize it's just for RSA.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1.
master/pkg/ssh/ssh.go
Outdated
generatedKeys = PrivateAndPublicKeys{ | ||
PrivateKey: pem.EncodeToMemory(block), | ||
PublicKey: sshlib.MarshalAuthorizedKey(publicKey), | ||
} | ||
|
||
return generatedKeys, nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit
generatedKeys = PrivateAndPublicKeys{ | |
PrivateKey: pem.EncodeToMemory(block), | |
PublicKey: sshlib.MarshalAuthorizedKey(publicKey), | |
} | |
return generatedKeys, nil | |
return PrivateAndPublicKeys{ | |
PrivateKey: pem.EncodeToMemory(block), | |
PublicKey: sshlib.MarshalAuthorizedKey(publicKey), | |
}, nil |
master/pkg/ssh/ssh.go
Outdated
} | ||
|
||
func generateED25519Key() (PrivateAndPublicKeys, error) { | ||
var generatedKeys PrivateAndPublicKeys |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same nit here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The nit being that this could skip being declared at all? If so, I agree. I'd rather see
return PrivateAndPublicKeys{}, errors.Wrap(err, "unable to generate ED25519 private key")
and then no generatedKeys
ever declared.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree
master/pkg/ssh/ssh_test.go
Outdated
} | ||
|
||
func TestSSHKeyGenerate(t *testing.T) { | ||
keys, err := GenerateKey(config.SSHConfig{KeyType: config.RSAKeyType, RsaKeySize: 512}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this test is so short it doesn't matter, but usually i'd recommend splitting a test like/using subtests with t.Run()
/ using table driven tests this up since it really is three separate tests. it's easier for whoever has to fix one of them if it fails to read just the one test that failed... but i also doubt this test will ever fail. this comment is just FYI.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Big +1 to table-driven tests and subtests.
i traded with max so he has time to review a pr of mine. |
master/internal/config/config.go
Outdated
// RSAKeyType uses RSA. | ||
RSAKeyType = "RSA" | ||
// ECDSAKeyType uses ECDSA. | ||
ECDSAKeyType = "ECDSA" | ||
// ED25519KeyType uses ED25519. | ||
ED25519KeyType = "ED25519" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Idiomatically, these names should be KeyTypeRSA
, KeyTypeECDSA
, and KeyTypeED25519
. A good example of this in the stdlib is net/http
, where all the methods and statuses are named this way.
master/internal/config/config.go
Outdated
if t.KeyType != RSAKeyType && t.KeyType != ECDSAKeyType && t.KeyType != ED25519KeyType { | ||
errs = append(errs, errors.New("Crypto system must be one of 'RSA', 'ECDSA' or 'ED25519'")) | ||
} | ||
if t.KeyType != RSAKeyType { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1.
master/pkg/ssh/ssh.go
Outdated
} | ||
|
||
func generateED25519Key() (PrivateAndPublicKeys, error) { | ||
var generatedKeys PrivateAndPublicKeys |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The nit being that this could skip being declared at all? If so, I agree. I'd rather see
return PrivateAndPublicKeys{}, errors.Wrap(err, "unable to generate ED25519 private key")
and then no generatedKeys
ever declared.
master/pkg/ssh/ssh_test.go
Outdated
} | ||
|
||
func TestSSHKeyGenerate(t *testing.T) { | ||
keys, err := GenerateKey(config.SSHConfig{KeyType: config.RSAKeyType, RsaKeySize: 512}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Big +1 to table-driven tests and subtests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
Ticket
MD-475
Description
Add master config option to specify which algorithm to use to generate ssh key.
Default is "RSA" for backward compatibility.
This PR also removes the
-p --passphrase
option fordet shell start
, since it does not make sense and it's been broken since at least 01/2023. More discussion here.Test Plan
Start master with one of the crypto system
Run
det shell start
There should be a message similar to
if "ECDSA" is the crypto system.
Checklist
docs/release-notes/
See Release Note for details.