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

-i flag for tsh ssh implementation. #1063

Closed
wants to merge 9 commits into from
Closed

-i flag for tsh ssh implementation. #1063

wants to merge 9 commits into from

Conversation

kontsevoy
Copy link
Contributor

@kontsevoy kontsevoy commented Jun 10, 2017

This PR implements #1033

@kontsevoy kontsevoy requested a review from russjones June 10, 2017 04:40
@kontsevoy
Copy link
Contributor Author

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.
@kontsevoy kontsevoy changed the title Cosmetic UX changes to tctl CLI -i flag for tsh ssh implementation. Jun 12, 2017
@kontsevoy
Copy link
Contributor Author

retest this please

caCert []byte
)
// read the identity file line by line:
scanner := bufio.NewScanner(f)
Copy link
Contributor

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:

  1. 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.
  2. 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 {
Copy link
Contributor

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?

return nil
}
}
err = trace.AccessDenied("untrusted host %v", hostId)
log.Error(err)
// final step: lets ask user:
Copy link
Contributor

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.

Copy link
Contributor

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?

Copy link
Contributor Author

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))
Copy link
Contributor

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() {
Copy link
Contributor

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
Copy link
Contributor

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?

@@ -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)

Copy link
Contributor

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?

Copy link
Contributor Author

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 {
Copy link
Contributor

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:
Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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

a.output = fmt.Sprintf("%s.pem", a.genUser)
}
var (
output io.Writer
Copy link
Contributor

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
@kontsevoy kontsevoy closed this Jun 12, 2017
@kontsevoy kontsevoy mentioned this pull request Jun 12, 2017
hatched pushed a commit that referenced this pull request Feb 1, 2023
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants