Added --compat=oldssh flag to generate user certificates without roles.

This commit is contained in:
Russell Jones 2017-06-19 17:20:21 -07:00
parent 64544a876b
commit 7e17b6f9a7
22 changed files with 211 additions and 68 deletions

View file

@ -166,3 +166,13 @@ const (
// MaxEnvironmentFileLines is the maximum number of lines in a environment file.
const MaxEnvironmentFileLines = 1000
const (
// CompatibilityOldSSH is used to make Teleport interoperate with older
// versions of OpenSSH.
CompatibilityOldSSH = "oldssh"
// CompatibilityNone is used for normal Teleport operation without any
// compatibility modes.
CompatibilityNone = ""
)

View file

@ -337,7 +337,7 @@ func (i *TeleInstance) CreateEx(trustedSecrets []*InstanceSecrets, tconf *servic
if err != nil {
return trace.Wrap(err)
}
user.Key.Cert, err = auth.GenerateUserCert(user.Key.Pub, teleUser, logins, ttl, true)
user.Key.Cert, err = auth.GenerateUserCert(user.Key.Pub, teleUser, logins, ttl, true, teleport.CompatibilityNone)
if err != nil {
return err
}

View file

@ -715,9 +715,10 @@ func (s *APIServer) generateHostCert(auth ClientI, w http.ResponseWriter, r *htt
}
type generateUserCertReq struct {
Key []byte `json:"key"`
User string `json:"user"`
TTL time.Duration `json:"ttl"`
Key []byte `json:"key"`
User string `json:"user"`
TTL time.Duration `json:"ttl"`
Compatibility string `json:"compatibility,omitempty"`
}
func (s *APIServer) generateUserCert(auth ClientI, w http.ResponseWriter, r *http.Request, _ httprouter.Params, version string) (interface{}, error) {
@ -725,7 +726,11 @@ func (s *APIServer) generateUserCert(auth ClientI, w http.ResponseWriter, r *htt
if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}
cert, err := auth.GenerateUserCert(req.Key, req.User, req.TTL)
compatibility, err := utils.CheckCompatibilityFlag(req.Compatibility)
if err != nil {
return nil, trace.Wrap(err)
}
cert, err := auth.GenerateUserCert(req.Key, req.User, req.TTL, compatibility)
if err != nil {
return nil, trace.Wrap(err)
}

View file

@ -235,7 +235,7 @@ func (s *APISuite) TestGenerateKeysAndCerts(c *C) {
authServer, userClient := s.newServerWithAuthorizer(c, authorizer)
defer authServer.Close()
cert, err = userClient.GenerateUserCert(pub, "user1", time.Hour)
cert, err = userClient.GenerateUserCert(pub, "user1", time.Hour, teleport.CompatibilityNone)
c.Assert(err, NotNil)
c.Assert(err.Error(), Equals, "auth API: access denied [00]")
@ -245,7 +245,7 @@ func (s *APISuite) TestGenerateKeysAndCerts(c *C) {
authServer2, userClient2 := s.newServerWithAuthorizer(c, authorizer)
defer authServer2.Close()
cert, err = userClient2.GenerateUserCert(pub, "user1", time.Hour)
cert, err = userClient2.GenerateUserCert(pub, "user1", time.Hour, teleport.CompatibilityNone)
c.Assert(err, NotNil)
c.Assert(err, ErrorMatches, ".*cannot request a certificate for user1")
@ -255,7 +255,7 @@ func (s *APISuite) TestGenerateKeysAndCerts(c *C) {
authServer3, userClient3 := s.newServerWithAuthorizer(c, authorizer)
defer authServer3.Close()
cert, err = userClient3.GenerateUserCert(pub, "user1", 40*time.Hour)
cert, err = userClient3.GenerateUserCert(pub, "user1", 40*time.Hour, teleport.CompatibilityNone)
c.Assert(err, IsNil)
parsedKey, _, _, _, err := ssh.ParseAuthorizedKey(cert)
c.Assert(err, IsNil)
@ -278,7 +278,7 @@ func (s *APISuite) TestGenerateKeysAndCerts(c *C) {
authServer4, userClient4 := s.newServerWithAuthorizer(c, authorizer)
defer authServer4.Close()
cert, err = userClient4.GenerateUserCert(pub, "user1", 1*time.Hour)
cert, err = userClient4.GenerateUserCert(pub, "user1", 1*time.Hour, teleport.CompatibilityNone)
c.Assert(err, IsNil)
parsedKey, _, _, _, err = ssh.ParseAuthorizedKey(cert)
c.Assert(err, IsNil)
@ -289,7 +289,7 @@ func (s *APISuite) TestGenerateKeysAndCerts(c *C) {
c.Assert(exists, Equals, true)
// apply HTTP Auth to generate user cert:
cert, err = userClient3.GenerateUserCert(pub, "user1", time.Hour)
cert, err = userClient3.GenerateUserCert(pub, "user1", time.Hour, teleport.CompatibilityNone)
c.Assert(err, IsNil)
_, _, _, _, err = ssh.ParseAuthorizedKey(cert)

View file

@ -215,7 +215,7 @@ func (s *AuthServer) GenerateHostCert(hostPublicKey []byte, hostID, nodeName, cl
// GenerateUserCert generates user certificate, it takes pkey as a signing
// private key (user certificate authority)
func (s *AuthServer) GenerateUserCert(key []byte, user services.User, allowedLogins []string, ttl time.Duration, canForwardAgents bool) ([]byte, error) {
func (s *AuthServer) GenerateUserCert(key []byte, user services.User, allowedLogins []string, ttl time.Duration, canForwardAgents bool, compatibility string) ([]byte, error) {
ca, err := s.Trust.GetCertAuthority(services.CertAuthID{
Type: services.UserCA,
DomainName: s.DomainName,
@ -228,13 +228,14 @@ func (s *AuthServer) GenerateUserCert(key []byte, user services.User, allowedLog
return nil, trace.Wrap(err)
}
return s.Authority.GenerateUserCert(services.UserCertParams{
PrivateCASigningKey: privateKey,
PublicUserKey: key,
Username: user.GetName(),
AllowedLogins: allowedLogins,
TTL: ttl,
PrivateCASigningKey: privateKey,
PublicUserKey: key,
Username: user.GetName(),
AllowedLogins: allowedLogins,
TTL: ttl,
Roles: user.GetRoles(),
Compatibility: compatibility,
PermitAgentForwarding: canForwardAgents,
Roles: user.GetRoles(),
})
}

View file

@ -365,7 +365,7 @@ func (a *AuthWithRoles) GenerateHostCert(
return a.authServer.GenerateHostCert(key, hostID, nodeName, clusterName, roles, ttl)
}
func (a *AuthWithRoles) GenerateUserCert(key []byte, username string, ttl time.Duration) ([]byte, error) {
func (a *AuthWithRoles) GenerateUserCert(key []byte, username string, ttl time.Duration, compatibility string) ([]byte, error) {
if err := a.currentUserAction(username); err != nil {
return nil, trace.AccessDenied("%v cannot request a certificate for %v", a.user.GetName(), username)
}
@ -398,7 +398,7 @@ func (a *AuthWithRoles) GenerateUserCert(key []byte, username string, ttl time.D
return nil, trace.Wrap(err)
}
return a.authServer.GenerateUserCert(
key, user, allowedLogins, sessionTTL, checker.CanForwardAgents())
key, user, allowedLogins, sessionTTL, checker.CanForwardAgents(), compatibility)
}
func (a *AuthWithRoles) CreateSignupToken(user services.UserV1) (token string, e error) {

View file

@ -742,17 +742,16 @@ func (c *Client) GenerateHostCert(
return []byte(cert), nil
}
// GenerateUserCert takes the public key in the Open SSH ``authorized_keys``
// plain text format, signs it using User Certificate Authority signing key and returns the
// resulting certificate.
func (c *Client) GenerateUserCert(
key []byte, user string, ttl time.Duration) ([]byte, error) {
// GenerateUserCert takes the public key in the OpenSSH `authorized_keys` plain
// text format, signs it using User Certificate Authority signing key and
// returns the resulting certificate.
func (c *Client) GenerateUserCert(key []byte, user string, ttl time.Duration, compatibility string) ([]byte, error) {
out, err := c.PostJSON(c.Endpoint("ca", "user", "certs"),
generateUserCertReq{
Key: key,
User: user,
TTL: ttl,
Key: key,
User: user,
TTL: ttl,
Compatibility: compatibility,
})
if err != nil {
return nil, trace.Wrap(err)
@ -1610,10 +1609,10 @@ type IdentityService interface {
// resulting certificate.
GenerateHostCert(key []byte, hostID, nodeName, clusterName string, roles teleport.Roles, ttl time.Duration) ([]byte, error)
// GenerateUserCert takes the public key in the Open SSH ``authorized_keys``
// GenerateUserCert takes the public key in the OpenSSH `authorized_keys`
// plain text format, signs it using User Certificate Authority signing key and returns the
// resulting certificate.
GenerateUserCert(key []byte, user string, ttl time.Duration) ([]byte, error)
GenerateUserCert(key []byte, user string, ttl time.Duration, compatibility string) ([]byte, error)
// GetSignupTokenData returns token data for a valid token
GetSignupTokenData(token string) (user string, otpQRCode []byte, e error)

View file

@ -209,18 +209,23 @@ func (n *nauth) GenerateUserCert(c services.UserCertParams) ([]byte, error) {
cert.Permissions.Extensions[teleport.CertExtensionPermitAgentForwarding] = ""
}
if len(c.Roles) != 0 {
roles, err := services.MarshalCertRoles(c.Roles)
if err != nil {
return nil, trace.Wrap(err)
// if we are requesting a certificate with support for older versions of OpenSSH
// don't add roles to certificate extensions, due to a bug in <= OpenSSH 7.1
// https://bugzilla.mindrot.org/show_bug.cgi?id=2387
if c.Compatibility != teleport.CompatibilityOldSSH {
roles, err := services.MarshalCertRoles(c.Roles)
if err != nil {
return nil, trace.Wrap(err)
}
cert.Permissions.Extensions[teleport.CertExtensionTeleportRoles] = roles
}
cert.Permissions.Extensions[teleport.CertExtensionTeleportRoles] = roles
}
signer, err := ssh.ParsePrivateKey(c.PrivateCASigningKey)
if err != nil {
return nil, err
return nil, trace.Wrap(err)
}
if err := cert.SignCert(rand.Reader, signer); err != nil {
return nil, err
return nil, trace.Wrap(err)
}
return ssh.MarshalAuthorizedKey(cert), nil
}

View file

@ -16,6 +16,7 @@ limitations under the License.
package native
import (
"fmt"
"testing"
"time"
@ -36,6 +37,7 @@ type NativeSuite struct {
}
var _ = Suite(&NativeSuite{})
var _ = fmt.Printf
func (s *NativeSuite) SetUpSuite(c *C) {
utils.InitLoggerForTests()
@ -139,3 +141,58 @@ func (s *NativeSuite) TestBuildPrincipals(c *C) {
c.Assert(hostCertificate.ValidPrincipals, DeepEquals, tt.outValidPrincipals)
}
}
// TestUserCertCompatibility makes sure the compatibility flag can be used to
// add to remove roles from certificate extensions.
func (s *NativeSuite) TestUserCertCompatibility(c *C) {
priv, pub, err := s.suite.A.GenerateKeyPair("")
c.Assert(err, IsNil)
tests := []struct {
inCompatibility string
outHasRoles bool
}{
// 0 - no compatibility, has roles
{
"",
true,
},
// 1 - no compatibility, has roles
{
"invalid",
true,
},
// 2 - compatibility, has roles
{
"oldssh",
false,
},
}
// run tests
for i, tt := range tests {
comment := Commentf("Test %v", i)
userCertificateBytes, err := s.suite.A.GenerateUserCert(services.UserCertParams{
PrivateCASigningKey: priv,
PublicUserKey: pub,
Username: "user",
AllowedLogins: []string{"centos", "root"},
TTL: time.Hour,
Roles: []string{"foo"},
Compatibility: tt.inCompatibility,
PermitAgentForwarding: true,
})
c.Assert(err, IsNil, comment)
publicKey, _, _, _, err := ssh.ParseAuthorizedKey(userCertificateBytes)
c.Assert(err, IsNil, comment)
userCertificate, ok := publicKey.(*ssh.Certificate)
c.Assert(ok, Equals, true, comment)
// check if we added the roles extension
_, ok = userCertificate.Extensions[teleport.CertExtensionTeleportRoles]
c.Assert(ok, Equals, tt.outHasRoles, comment)
}
}

View file

@ -218,7 +218,7 @@ func (a *AuthServer) ValidateOIDCAuthCallback(q url.Values) (*OIDCAuthResponse,
if err != nil {
return nil, trace.Wrap(err)
}
cert, err := a.GenerateUserCert(req.PublicKey, user, allowedLogins, certTTL, roles.CanForwardAgents())
cert, err := a.GenerateUserCert(req.PublicKey, user, allowedLogins, certTTL, roles.CanForwardAgents(), req.Compatibility)
if err != nil {
return nil, trace.Wrap(err)
}

View file

@ -315,7 +315,7 @@ func (a *AuthServer) ValidateSAMLResponse(samlResponse string) (*SAMLAuthRespons
if err != nil {
return nil, trace.Wrap(err)
}
cert, err := a.GenerateUserCert(request.PublicKey, user, allowedLogins, certTTL, roles.CanForwardAgents())
cert, err := a.GenerateUserCert(request.PublicKey, user, allowedLogins, certTTL, roles.CanForwardAgents(), request.Compatibility)
if err != nil {
return nil, trace.Wrap(err)
}

View file

@ -172,6 +172,9 @@ type Config struct {
// CachePolicy defines local caching policy in case if discovery goes down
// by default does not use caching
CachePolicy *CachePolicy
// Compatibility specifies OpenSSH compatibility flags.
Compatibility string
}
// CachePolicy defines cache policy for local clients
@ -1010,7 +1013,8 @@ func (tc *TeleportClient) Login() (*CertAuthMethod, error) {
return nil, trace.Wrap(err)
}
// generate a new keypair. the public key will be signed via proxy if our password+HOTP are legit
// generate a new keypair. the public key will be signed via proxy if our
// password+OTP are legit
key, err := tc.MakeKey()
if err != nil {
return nil, trace.Wrap(err)
@ -1124,14 +1128,16 @@ func (tc *TeleportClient) directLogin(secondFactorType string, pub []byte) (*SSH
}
// ask the CA (via proxy) to sign our public key:
response, err := SSHAgentLogin(httpsProxyHostPort,
response, err := SSHAgentLogin(
httpsProxyHostPort,
tc.Config.Username,
password,
otpToken,
pub,
tc.KeyTTL,
tc.InsecureSkipVerify,
certPool)
certPool,
tc.Compatibility)
return response, trace.Wrap(err)
}
@ -1141,8 +1147,15 @@ func (tc *TeleportClient) ssoLogin(connectorID string, pub []byte, protocol stri
log.Debugf("samlLogin start")
// ask the CA (via proxy) to sign our public key:
webProxyAddr := tc.Config.ProxyWebHostPort()
response, err := SSHAgentSSOLogin(webProxyAddr,
connectorID, pub, tc.KeyTTL, tc.InsecureSkipVerify, loopbackPool(webProxyAddr), protocol)
response, err := SSHAgentSSOLogin(
webProxyAddr,
connectorID,
pub,
tc.KeyTTL,
tc.InsecureSkipVerify,
loopbackPool(webProxyAddr),
protocol,
tc.Compatibility)
return response, trace.Wrap(err)
}
@ -1162,13 +1175,15 @@ func (tc *TeleportClient) u2fLogin(pub []byte) (*SSHLoginResponse, error) {
return nil, trace.Wrap(err)
}
response, err := SSHAgentU2FLogin(httpsProxyHostPort,
response, err := SSHAgentU2FLogin(
httpsProxyHostPort,
tc.Config.Username,
password,
pub,
tc.KeyTTL,
tc.InsecureSkipVerify,
certPool)
certPool,
tc.Compatibility)
return response, trace.Wrap(err)
}

View file

@ -61,10 +61,11 @@ type SSHLoginResponse struct {
// SSOLoginConsoleReq is used to SSO for tsh
type SSOLoginConsoleReq struct {
RedirectURL string `json:"redirect_url"`
PublicKey []byte `json:"public_key"`
CertTTL time.Duration `json:"cert_ttl"`
ConnectorID string `json:"connector_id"`
RedirectURL string `json:"redirect_url"`
PublicKey []byte `json:"public_key"`
CertTTL time.Duration `json:"cert_ttl"`
ConnectorID string `json:"connector_id"`
Compatibility string `json:"compatibility,omitempty"`
}
// SSOLoginConsoleResponse is a response to SSO console request
@ -96,6 +97,8 @@ type CreateSSHCertReq struct {
// TTL is a desired TTL for the cert (max is still capped by server,
// however user can shorten the time)
TTL time.Duration `json:"ttl"`
// Compatibility specifies OpenSSH compatibility flags.
Compatibility string `json:"compatibility,omitempty"`
}
// CreateSSHCertWithU2FReq are passed by web client
@ -112,6 +115,8 @@ type CreateSSHCertWithU2FReq struct {
// TTL is a desired TTL for the cert (max is still capped by server,
// however user can shorten the time)
TTL time.Duration `json:"ttl"`
// Compatibility specifies OpenSSH compatibility flags.
Compatibility string `json:"compatibility,omitempty"`
}
type sealData struct {
@ -120,7 +125,7 @@ type sealData struct {
}
// SSHAgentSSOLogin is used by SSH Agent (tsh) to login using OpenID connect
func SSHAgentSSOLogin(proxyAddr, connectorID string, pubKey []byte, ttl time.Duration, insecure bool, pool *x509.CertPool, protocol string) (*SSHLoginResponse, error) {
func SSHAgentSSOLogin(proxyAddr, connectorID string, pubKey []byte, ttl time.Duration, insecure bool, pool *x509.CertPool, protocol string, compatibility string) (*SSHLoginResponse, error) {
clt, proxyURL, err := initClient(proxyAddr, insecure, pool)
if err != nil {
return nil, trace.Wrap(err)
@ -200,10 +205,11 @@ func SSHAgentSSOLogin(proxyAddr, connectorID string, pubKey []byte, ttl time.Dur
u.RawQuery = query.Encode()
out, err := clt.PostJSON(clt.Endpoint("webapi", protocol, "login", "console"), SSOLoginConsoleReq{
RedirectURL: u.String(),
PublicKey: pubKey,
CertTTL: ttl,
ConnectorID: connectorID,
RedirectURL: u.String(),
PublicKey: pubKey,
CertTTL: ttl,
ConnectorID: connectorID,
Compatibility: compatibility,
})
if err != nil {
return nil, trace.Wrap(err)
@ -315,17 +321,18 @@ func Ping(proxyAddr string, insecure bool, pool *x509.CertPool) (*PingResponse,
// if credentials are valid
//
// proxyAddr must be specified as host:port
func SSHAgentLogin(proxyAddr, user, password, otpToken string, pubKey []byte, ttl time.Duration, insecure bool, pool *x509.CertPool) (*SSHLoginResponse, error) {
func SSHAgentLogin(proxyAddr, user, password, otpToken string, pubKey []byte, ttl time.Duration, insecure bool, pool *x509.CertPool, compatibility string) (*SSHLoginResponse, error) {
clt, _, err := initClient(proxyAddr, insecure, pool)
if err != nil {
return nil, trace.Wrap(err)
}
re, err := clt.PostJSON(clt.Endpoint("webapi", "ssh", "certs"), CreateSSHCertReq{
User: user,
Password: password,
OTPToken: otpToken,
PubKey: pubKey,
TTL: ttl,
User: user,
Password: password,
OTPToken: otpToken,
PubKey: pubKey,
TTL: ttl,
Compatibility: compatibility,
})
if err != nil {
return nil, trace.Wrap(err)
@ -344,7 +351,7 @@ func SSHAgentLogin(proxyAddr, user, password, otpToken string, pubKey []byte, tt
// If the credentials are valid, the proxy wiil return a challenge.
// We then call the official u2f-host binary to perform the signing and pass the signature to the proxy.
// If the authentication succeeds, we will get a temporary certificate back
func SSHAgentU2FLogin(proxyAddr, user, password string, pubKey []byte, ttl time.Duration, insecure bool, pool *x509.CertPool) (*SSHLoginResponse, error) {
func SSHAgentU2FLogin(proxyAddr, user, password string, pubKey []byte, ttl time.Duration, insecure bool, pool *x509.CertPool, compatibility string) (*SSHLoginResponse, error) {
clt, _, err := initClient(proxyAddr, insecure, pool)
if err != nil {
return nil, trace.Wrap(err)
@ -414,6 +421,7 @@ func SSHAgentU2FLogin(proxyAddr, user, password string, pubKey []byte, ttl time.
U2FSignResponse: *u2fSignResponse,
PubKey: pubKey,
TTL: ttl,
Compatibility: compatibility,
})
if err != nil {
return nil, trace.Wrap(err)

View file

@ -61,6 +61,8 @@ type UserCertParams struct {
PermitAgentForwarding bool
// Roles is a list of roles assigned to this user
Roles []string
// Compatibility specifies OpenSSH compatibility flags.
Compatibility string
}
// CertRoles defines certificate roles

View file

@ -285,6 +285,9 @@ type OIDCAuthRequest struct {
// ClientRedirectURL is a URL client wants to be redirected
// after successfull authentication
ClientRedirectURL string `json:"client_redirect_url"`
// Compatibility specifies OpenSSH compatibility flags.
Compatibility string `json:"compatibility,omitempty"`
}
// Check returns nil if all parameters are great, err otherwise
@ -341,6 +344,9 @@ type SAMLAuthRequest struct {
// ClientRedirectURL is a URL client wants to be redirected
// after successfull authentication
ClientRedirectURL string `json:"client_redirect_url"`
// Compatibility specifies OpenSSH compatibility flags.
Compatibility string `json:"compatibility,omitempty"`
}
// Check returns nil if all parameters are great, err otherwise

View file

@ -929,7 +929,7 @@ func newUpack(username string, allowedLogins []string, a *auth.AuthServer) (*upa
return nil, trace.Wrap(err)
}
ucert, err := a.GenerateUserCert(upub, user, allowedLogins, 0, true)
ucert, err := a.GenerateUserCert(upub, user, allowedLogins, 0, true, teleport.CompatibilityNone)
if err != nil {
return nil, trace.Wrap(err)
}

View file

@ -203,6 +203,16 @@ func SliceContainsStr(slice []string, value string) bool {
return false
}
// CheckCompatibilityFlag check that the compatibility flag is valid.
func CheckCompatibilityFlag(s string) (string, error) {
switch s {
case teleport.CompatibilityNone, teleport.CompatibilityOldSSH:
return s, nil
default:
return teleport.CompatibilityNone, trace.BadParameter("invalid compatibility parameter: %q", s)
}
}
const (
// HumanTimeFormatString is a human readable date formatting
HumanTimeFormatString = "Mon Jan _2 15:04 UTC"

View file

@ -526,6 +526,7 @@ func (m *Handler) oidcLoginConsole(w http.ResponseWriter, r *http.Request, p htt
PublicKey: req.PublicKey,
CertTTL: req.CertTTL,
CheckUser: true,
Compatibility: req.Compatibility,
})
if err != nil {
return nil, trace.Wrap(err)

View file

@ -61,6 +61,7 @@ func (m *Handler) samlSSOConsole(w http.ResponseWriter, r *http.Request, p httpr
ClientRedirectURL: req.RedirectURL,
PublicKey: req.PublicKey,
CertTTL: req.CertTTL,
Compatibility: req.Compatibility,
})
if err != nil {
return nil, trace.Wrap(err)

View file

@ -325,7 +325,7 @@ func (s *sessionCache) GetCertificateWithoutOTP(c client.CreateSSHCertReq) (*cli
}
defer clt.Close()
return createCertificate(c.User, c.PubKey, c.TTL, clt)
return createCertificate(c.User, c.PubKey, c.TTL, c.Compatibility, clt)
}
func (s *sessionCache) GetCertificateWithOTP(c client.CreateSSHCertReq) (*client.SSHLoginResponse, error) {
@ -340,11 +340,11 @@ func (s *sessionCache) GetCertificateWithOTP(c client.CreateSSHCertReq) (*client
}
defer clt.Close()
return createCertificate(c.User, c.PubKey, c.TTL, clt)
return createCertificate(c.User, c.PubKey, c.TTL, c.Compatibility, clt)
}
func createCertificate(user string, pubkey []byte, ttl time.Duration, clt *auth.TunClient) (*client.SSHLoginResponse, error) {
cert, err := clt.GenerateUserCert(pubkey, user, ttl)
func createCertificate(user string, pubkey []byte, ttl time.Duration, compatibility string, clt *auth.TunClient) (*client.SSHLoginResponse, error) {
cert, err := clt.GenerateUserCert(pubkey, user, ttl, compatibility)
if err != nil {
return nil, trace.Wrap(err)
}
@ -368,12 +368,14 @@ func (s *sessionCache) GetCertificateWithU2F(c client.CreateSSHCertWithU2FReq) (
if err != nil {
return nil, trace.Wrap(err)
}
clt, err := auth.NewTunClient("web.session-u2f", s.authServers, c.User, method)
if err != nil {
return nil, trace.Wrap(err)
}
defer clt.Close()
return createCertificate(c.User, c.PubKey, c.TTL, clt)
return createCertificate(c.User, c.PubKey, c.TTL, c.Compatibility, clt)
}
func (s *sessionCache) GetUserInviteInfo(token string) (user string, otpQRCode []byte, err error) {

View file

@ -92,6 +92,7 @@ type AuthCommand struct {
output string
outputFormat string
compatVersion string
compatibility string
}
const (
@ -243,6 +244,7 @@ func Run() {
authSign.Flag("out", "identity output").Short('o').StringVar(&cmdAuth.output)
authSign.Flag("format", "identity format: 'file' (default) or 'dir'").Default(DefaultIdentityFormat).StringVar(&cmdAuth.outputFormat)
authSign.Flag("ttl", "TTL (time to live) for the generated certificate").Default(fmt.Sprintf("%v", defaults.CertDuration)).DurationVar(&cmdAuth.genTTL)
authSign.Flag("compat", "OpenSSH compatibility flag").StringVar(&cmdAuth.compatibility)
// operations with reverse tunnels
reverseTunnels := app.Command("tunnels", "Operations on reverse tunnels clusters").Hidden()
@ -745,7 +747,14 @@ func (a *AuthCommand) GenerateAndSignKeys(client *auth.TunClient) error {
if err != nil {
return trace.Wrap(err)
}
cert, err := client.GenerateUserCert(publicKey, a.genUser, a.genTTL)
// parse compatibility parameter
compatibility, err := utils.CheckCompatibilityFlag(a.compatibility)
if err != nil {
return trace.Wrap(err)
}
cert, err := client.GenerateUserCert(publicKey, a.genUser, a.genTTL, compatibility)
if err != nil {
return trace.Wrap(err)
}

View file

@ -109,6 +109,8 @@ type CLIConf struct {
GopsAddr string
// IdentityFile is an argument to -i flag (path to the private key+cert file)
IdentityFile string
// Compatibility flags, --compat, specifies OpenSSH compatibility flags.
Compatibility string
}
// Run executes TSH client. same as main() but easier to test
@ -127,6 +129,7 @@ func Run(args []string, underTest bool) {
app.Flag("cluster", "Specify the cluster to connect").Envar("TELEPORT_SITE").StringVar(&cf.SiteName)
app.Flag("ttl", "Minutes to live for a SSH session").Int32Var(&cf.MinsToLive)
app.Flag("identity", "Identity file").Short('i').StringVar(&cf.IdentityFile)
app.Flag("compat", "OpenSSH compatibility flag").StringVar(&cf.Compatibility)
app.Flag("insecure", "Do not verify server's certificate and host name. Use only in test environments").Default("false").BoolVar(&cf.InsecureSkipVerify)
app.Flag("namespace", "Namespace of the cluster").Default(defaults.Namespace).Hidden().StringVar(&cf.Namespace)
@ -260,6 +263,7 @@ func onLogin(cf *CLIConf) {
if err != nil {
utils.FatalError(err)
}
if _, err := tc.Login(); err != nil {
utils.FatalError(err)
}
@ -588,6 +592,14 @@ func makeClient(cf *CLIConf, useProfileLogin bool) (tc *client.TeleportClient, e
if !cf.NoCache {
c.CachePolicy = &client.CachePolicy{}
}
// parse compatibility parameter
compatibility, err := utils.CheckCompatibilityFlag(cf.Compatibility)
if err != nil {
return nil, trace.Wrap(err)
}
c.Compatibility = compatibility
return client.NewClient(c)
}