mirror of
https://github.com/gravitational/teleport.git
synced 2026-09-24 16:17:11 +08:00
177 lines
4.7 KiB
Go
177 lines
4.7 KiB
Go
/*
|
|
Copyright 2015 Gravitational, Inc.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
// package auth implements certificate signing authority and access control server
|
|
// Authority server is composed of several parts:
|
|
//
|
|
// * Authority server itself that implements signing and acl logic
|
|
// * HTTP server wrapper for authority server
|
|
// * HTTP client wrapper
|
|
//
|
|
|
|
package client
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"net"
|
|
"path/filepath"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/gravitational/teleport/lib/auth/native"
|
|
"github.com/gravitational/teleport/lib/utils"
|
|
"github.com/gravitational/teleport/lib/web"
|
|
|
|
"github.com/gravitational/trace"
|
|
"golang.org/x/crypto/ssh"
|
|
"golang.org/x/crypto/ssh/agent"
|
|
)
|
|
|
|
func AuthMethodFromAgent(ag agent.Agent) ssh.AuthMethod {
|
|
return ssh.PublicKeysCallback(ag.Signers)
|
|
}
|
|
|
|
// NewWebAuth returns ssh.AuthMethod as a callback function and SSH HostKeyCallback.
|
|
// When any of them is called it tries to generate certificates using password and
|
|
// hotpToken and adds the login certificate to the provided agent, saves the
|
|
// certificates to the local folder and returns the agent as authenticator.
|
|
func NewWebAuth(ag agent.Agent,
|
|
user string,
|
|
passwordCallback PasswordCallback,
|
|
webProxyAddress string,
|
|
certificateTTL time.Duration,
|
|
insecure bool) (authMethod ssh.AuthMethod, hostKeyCallback utils.HostKeyCallback) {
|
|
|
|
callbackFunc := func() (signers []ssh.Signer, err error) {
|
|
err = Login(ag, webProxyAddress, user, certificateTTL, passwordCallback, insecure)
|
|
if err != nil {
|
|
fmt.Printf("Can't login to the server: %v\n", err)
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
return ag.Signers()
|
|
}
|
|
|
|
hostKeyCallback = func(hostID string, remote net.Addr, key ssh.PublicKey) error {
|
|
err := CheckHostSignerFromCache(hostID, remote, key)
|
|
if err != nil {
|
|
err = Login(ag, webProxyAddress, user, certificateTTL, passwordCallback, insecure)
|
|
if err != nil {
|
|
fmt.Printf("Can't login to %v\n", err)
|
|
return trace.Wrap(err)
|
|
}
|
|
return CheckHostSignerFromCache(hostID, remote, key)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
return ssh.PublicKeysCallback(callbackFunc), hostKeyCallback
|
|
}
|
|
|
|
type PasswordCallback func() (password, hotpToken string, e error)
|
|
|
|
// Login tries to generate
|
|
// teleport certificate using password and hotpToken, adds the
|
|
// certificate to the provided agent and saves the certificate to the
|
|
// local folder.
|
|
func Login(ag agent.Agent, webProxyAddr string, user string,
|
|
ttl time.Duration, passwordCallback PasswordCallback, insecure bool) error {
|
|
|
|
password, hotpToken, err := passwordCallback()
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return trace.Wrap(err)
|
|
}
|
|
|
|
fmt.Printf("Logging in...\n")
|
|
|
|
priv, pub, err := native.New().GenerateKeyPair("")
|
|
if err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
|
|
login, err := web.SSHAgentLogin(webProxyAddr, user, password, hotpToken,
|
|
pub, ttl, insecure)
|
|
if err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
|
|
pcert, _, _, _, err := ssh.ParseAuthorizedKey(login.Cert)
|
|
if err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
|
|
pk, err := ssh.ParseRawPrivateKey(priv)
|
|
if err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
addedKey := agent.AddedKey{
|
|
PrivateKey: pk,
|
|
Certificate: pcert.(*ssh.Certificate),
|
|
Comment: "",
|
|
LifetimeSecs: 0,
|
|
ConfirmBeforeUse: false,
|
|
}
|
|
if err := ag.Add(addedKey); err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
|
|
key := Key{
|
|
Priv: priv,
|
|
Cert: login.Cert,
|
|
Deadline: time.Now().Add(ttl),
|
|
}
|
|
|
|
keyID := int64(time.Now().Sub(time.Time{}).Seconds())*100 + rand.Int63n(100)
|
|
keyPath := filepath.Join(getKeysDir(),
|
|
KeyFilePrefix+strconv.FormatInt(keyID, 16)+KeyFileSuffix)
|
|
|
|
err = saveKey(key, keyPath)
|
|
if err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
|
|
err = AddHostSignersToCache(login.HostSigners)
|
|
if err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
|
|
fmt.Println("Logged in successfully")
|
|
return nil
|
|
}
|
|
|
|
// GetPasswordFromConsole returns a function which requests password+HOTP token
|
|
// from the console
|
|
func GetPasswordFromConsole(user string) PasswordCallback {
|
|
return func() (password, hotpToken string, e error) {
|
|
fmt.Printf("Enter password for %v:\n", user)
|
|
password, err := passwordFromConsole()
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return "", "", trace.Wrap(err)
|
|
}
|
|
|
|
fmt.Printf("Enter your HOTP token:\n")
|
|
hotpToken, err = passwordFromConsole()
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return "", "", trace.Wrap(err)
|
|
}
|
|
|
|
return password, hotpToken, nil
|
|
}
|
|
}
|