-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
-i flag for tsh ssh
implementation.
#1063
Conversation
This commit does not change tctl behavior
retest this please |
First part of addressing #1033 is ability to load credentials from the credentials file(s). This commit adds -i flag processing, i.e. a certificate can be fed via a cert.file and used to login.
Hooked up -i auth into the proper place
tsh ssh
implementation.
retest this please |
caCert []byte | ||
) | ||
// read the identity file line by line: | ||
scanner := bufio.NewScanner(f) |
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.
Do we control the format of the identity file? I see that we use tctl auth sign
to generate it. If that's the case, what do you think about being more strict about the format, something like this:
Line 1: User public in authorized_keys format.
Line 2: Host public key in known_hosts format.
Line 3-n: User private key in PEM format.
A couple of advantages of this:
- This making parsing this file much easier. You pull off the first line and process it. Pull off the second line and process it. Then take the rest of the input and process it as a PEM file.
- OpenSSH interoperability because OpenSSH stores user and host public keys in different formats.
} | ||
// found CA cert in the indentity file? construct the host key checking function | ||
// and return it: | ||
hostAuthFunc = func(host string, a net.Addr, hostKey ssh.PublicKey) error { |
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.
Why not add the certificate to ~/.tsh/known_hosts
here? If the user is explicitly passing in a identity with the -i
flag, they're effectively bypassing the interactive parts of the Teleport login. A successful interactive login ends up with the user having a signed certificate plus the certificate of the CA being added to ~/.tsh/known_hosts
so shouldn't -i
do the same thing?
lib/client/keyagent.go
Outdated
return nil | ||
} | ||
} | ||
err = trace.AccessDenied("untrusted host %v", hostId) | ||
log.Error(err) | ||
// final step: lets ask user: |
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 you update the comment here to something like: "final step: we have not seen this host certificate before, prompt the user, then add to known hosts".
On my first pass I was a little confused why we were always asking the user in the final step when they had a certificate. I missed the return nil
a few lines up because typically you return an error in the body and return nil
(success) at the end.
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.
Actually it appears we now do always prompt users if they trust the host?
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.
added the comments, fixed the 3rd party agent-stored keys issue
if !a.noHosts[host] { | ||
fmt.Printf("The authenticity of host '%s' can't be established. "+ | ||
"Its public key is:\n%s\nAre you sure you want to continue (yes/no)? ", | ||
host, ssh.MarshalAuthorizedKey(key)) |
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'm curious why you went with authorized_keys
format here over known_hosts
format? Also why not a key fingerprint like OpenSSH does?
if tc.Config.SkipLocalAuth { | ||
return nil, trace.BadParameter("failed to authenticate with proxy %v", proxyAddr) | ||
// is disabled in configuration, or the user refused connecting to untrusted hosts | ||
if tc.Config.SkipLocalAuth || tc.localAgent.UserRefusedHosts() { |
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 might be useful to have these in separate blocks then return different errors. Knowing if authentication failed because because all auth methods were exhausted or because of an invalid host certificate would be useful when debugging issues.
|
||
// map of "no hosts". these are hosts that user manually (via keyboard | ||
// input) refused connecting to. | ||
noHosts map[string]bool |
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.
When would noHosts
be used? It doesn't appear to be persisted to disk and isn't LocalKeyAgent
recreated everytime you run tsh
?
lib/client/keyagent.go
Outdated
@@ -258,7 +292,7 @@ func (a *LocalKeyAgent) AddKey(host string, username string, key *Key) (*CertAut | |||
// save it to disk (usually into ~/.tsh) | |||
err := a.keyStore.AddKey(host, username, key) | |||
if err != nil { | |||
return nil, trace.Wrap(err) | |||
|
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 think you might have removed this line accidentally?
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.
fixed
defer f.Close() | ||
} | ||
// write key: | ||
if _, err = output.Write(privateKey); err != 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.
Not sure how I feel about this, in general I think it's better to generate the private key on the client and then send the public key to be signed by the CA.
// | ||
// dump user identity into a single file: | ||
// | ||
case IdentityFormatFile: |
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'm not seeing the host certificate being exported and as far as I can tell the --type
flag does not exist? Because the following from the issue does not work: tctl auth sign --type=host
.
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.
Also, we should probably add some documentation for how to use this feature to the Admin Guide.
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.
tctl
now dumps the identity to stdout
by default but you can set -o
to something else
tool/tctl/common/tctl.go
Outdated
a.output = fmt.Sprintf("%s.pem", a.genUser) | ||
} | ||
var ( | ||
output io.Writer |
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 isn't really consistent with how tctl
has worked up to this point, tctl auth export
and tctl saml export
output to stdout
and we redirect to a file.
key-agent changes: tsh now ignores public keys loaded from the SSH key agent unless they are certificates. this fixes the problem of 'host auth' callback being called for 3rd party public keys in the SSH-agent (and tsh had no choice but to prompt user if he wanted to add thoses hosts to known_hosts file) tctl auth changes: `tctl auth` now outputs to stdout by default. If you set -o to a file, it will use that file instead. Getting rid of -o was not possible because it's still useful if --format is set to `dir` (OpenSSH compatible output). other changes: - added more detailed comments in a few places - restored error handling logic
If '0' is provided as the default and the initial call to create a gateway fails, the user will be presented with an input that says 0. 0 is an invalid value though that this input will not accept. An empty string, on the other hand, is a perfectly valid value (the input doesn't have the `required` attribute). In that scenario, the backend will default the value to '0' and pick a random port.
This PR implements #1033