adding basic support for OpenSSH CertificateFile support
small fix
This commit is contained in:
parent
a15ee80c68
commit
31a7a1d637
|
@ -0,0 +1,73 @@
|
|||
package ssh_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/user"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
helperssh "github.com/hashicorp/packer/helper/ssh"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
func getIdentityCertFile() (certSigner ssh.Signer, err error) {
|
||||
usr, _ := user.Current()
|
||||
privateKeyFile := usr.HomeDir + "/.ssh/id_ed25519"
|
||||
certificateFile := usr.HomeDir + "/.ssh/id_ed25519-cert.pub"
|
||||
|
||||
return helperssh.FileSignerWithCert(privateKeyFile, certificateFile)
|
||||
}
|
||||
|
||||
func TestConnectFunc(t *testing.T) {
|
||||
{
|
||||
if os.Getenv("PACKER_ACC") == "" {
|
||||
t.Skip("This test is only run with PACKER_ACC=1")
|
||||
}
|
||||
|
||||
const host = "mybastionhost.com:2222"
|
||||
|
||||
certSigner, err := getIdentityCertFile()
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("we have an error %v", err))
|
||||
}
|
||||
|
||||
publicKeys := ssh.PublicKeys(certSigner)
|
||||
usr, _ := user.Current()
|
||||
|
||||
config := &ssh.ClientConfig{
|
||||
User: usr.Username,
|
||||
Auth: []ssh.AuthMethod{
|
||||
publicKeys,
|
||||
},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
Timeout: 30 * time.Second,
|
||||
}
|
||||
|
||||
println("Dialing", config.User)
|
||||
connection, err := ssh.Dial("tcp", host, config)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("Failed to dial ", err)
|
||||
return
|
||||
}
|
||||
|
||||
session, err := connection.NewSession()
|
||||
if err != nil {
|
||||
log.Fatal("Failed to create session: ", err)
|
||||
return
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
var stdoutBuf bytes.Buffer
|
||||
session.Stdout = &stdoutBuf
|
||||
|
||||
err = session.Run("ls")
|
||||
if err != nil {
|
||||
log.Fatal("Failed to ls")
|
||||
}
|
||||
fmt.Println(stdoutBuf.String())
|
||||
}
|
||||
}
|
|
@ -111,6 +111,10 @@ type SSH struct {
|
|||
// The `~` can be used in path and will be expanded to the home directory
|
||||
// of current user.
|
||||
SSHPrivateKeyFile string `mapstructure:"ssh_private_key_file" undocumented:"true"`
|
||||
// Path to user certificate used to authenticate with SSH.
|
||||
// The `~` can be used in path and will be expanded to the
|
||||
// home directory of current user.
|
||||
SSHCertificateFile string `mapstructure:"ssh_certificate_file"`
|
||||
// If `true`, a PTY will be requested for the SSH connection. This defaults
|
||||
// to `false`.
|
||||
SSHPty bool `mapstructure:"ssh_pty"`
|
||||
|
@ -148,6 +152,10 @@ type SSH struct {
|
|||
// bastion host. The `~` can be used in path and will be expanded to the
|
||||
// home directory of current user.
|
||||
SSHBastionPrivateKeyFile string `mapstructure:"ssh_bastion_private_key_file"`
|
||||
// Path to user certificate used to authenticate with bastion host.
|
||||
// The `~` can be used in path and will be expanded to the
|
||||
//home directory of current user.
|
||||
SSHBastionCertificateFile string `mapstructure:"ssh_bastion_certificate_file"`
|
||||
// `scp` or `sftp` - How to transfer files, Secure copy (default) or SSH
|
||||
// File Transfer Protocol.
|
||||
SSHFileTransferMethod string `mapstructure:"ssh_file_transfer_method"`
|
||||
|
@ -316,11 +324,29 @@ func (c *Config) SSHConfigFunc() func(multistep.StateBag) (*ssh.ClientConfig, er
|
|||
privateKeys = append(privateKeys, c.SSHPrivateKey)
|
||||
}
|
||||
|
||||
certPath := ""
|
||||
if c.SSHCertificateFile != "" {
|
||||
var err error
|
||||
certPath, err = packer.ExpandUser(c.SSHCertificateFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, key := range privateKeys {
|
||||
|
||||
signer, err := ssh.ParsePrivateKey(key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error on parsing SSH private key: %s", err)
|
||||
}
|
||||
|
||||
if certPath != "" {
|
||||
signer, err = helperssh.ReadCertificate(certPath, signer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
sshConfig.Auth = append(sshConfig.Auth, ssh.PublicKeys(signer))
|
||||
}
|
||||
|
||||
|
@ -431,6 +457,11 @@ func (c *Config) prepareSSH(ctx *interpolate.Context) []error {
|
|||
if c.SSHBastionPrivateKeyFile == "" && c.SSHPrivateKeyFile != "" {
|
||||
c.SSHBastionPrivateKeyFile = c.SSHPrivateKeyFile
|
||||
}
|
||||
|
||||
if c.SSHBastionCertificateFile == "" && c.SSHCertificateFile != "" {
|
||||
c.SSHBastionCertificateFile = c.SSHCertificateFile
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if c.SSHProxyHost != "" {
|
||||
|
@ -462,9 +493,23 @@ func (c *Config) prepareSSH(ctx *interpolate.Context) []error {
|
|||
} else if _, err := os.Stat(path); err != nil {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"ssh_private_key_file is invalid: %s", err))
|
||||
} else if _, err := helperssh.FileSigner(path); err != nil {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"ssh_private_key_file is invalid: %s", err))
|
||||
} else {
|
||||
if c.SSHCertificateFile != "" {
|
||||
certPath, err := packer.ExpandUser(c.SSHCertificateFile)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("invalid identity certificate: #{err}"))
|
||||
}
|
||||
|
||||
if _, err := helperssh.FileSignerWithCert(path, certPath); err != nil {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"ssh_private_key_file is invalid: %s", err))
|
||||
}
|
||||
} else {
|
||||
if _, err := helperssh.FileSigner(path); err != nil {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"ssh_private_key_file is invalid: %s", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -480,9 +525,22 @@ func (c *Config) prepareSSH(ctx *interpolate.Context) []error {
|
|||
} else if _, err := os.Stat(path); err != nil {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"ssh_bastion_private_key_file is invalid: %s", err))
|
||||
} else if _, err := helperssh.FileSigner(path); err != nil {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"ssh_bastion_private_key_file is invalid: %s", err))
|
||||
} else {
|
||||
if c.SSHBastionCertificateFile != "" {
|
||||
certPath, err := packer.ExpandUser(c.SSHBastionCertificateFile)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("invalid identity certificate: #{err}"))
|
||||
}
|
||||
if _, err := helperssh.FileSignerWithCert(path, certPath); err != nil {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"ssh_bastion_private_key_file is invalid: %s", err))
|
||||
}
|
||||
} else {
|
||||
if _, err := helperssh.FileSigner(path); err != nil {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"ssh_bastion_private_key_file is invalid: %s", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ type FlatConfig struct {
|
|||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
|
@ -33,6 +34,7 @@ type FlatConfig struct {
|
|||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
|
@ -78,6 +80,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
||||
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
||||
"ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
|
||||
"ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
|
||||
"ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false},
|
||||
|
@ -91,6 +94,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||
"ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
|
||||
"ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false},
|
||||
"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
|
||||
"ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
|
||||
"ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
|
||||
|
@ -127,6 +131,7 @@ type FlatSSH struct {
|
|||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
|
@ -140,6 +145,7 @@ type FlatSSH struct {
|
|||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
|
@ -174,6 +180,7 @@ func (*FlatSSH) HCL2Spec() map[string]hcldec.Spec {
|
|||
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
||||
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
||||
"ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
|
||||
"ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
|
||||
"ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false},
|
||||
|
@ -187,6 +194,7 @@ func (*FlatSSH) HCL2Spec() map[string]hcldec.Spec {
|
|||
"ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
|
||||
"ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false},
|
||||
"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
|
||||
"ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
|
||||
"ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
|
||||
|
|
|
@ -139,6 +139,30 @@ func TestConfig_winrm_use_ntlm(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestSSHBastion(t *testing.T) {
|
||||
c := &Config{
|
||||
Type: "ssh",
|
||||
SSH: SSH{
|
||||
SSHUsername: "root",
|
||||
SSHBastionHost: "mybastionhost.company.com",
|
||||
SSHBastionPassword: "test",
|
||||
},
|
||||
}
|
||||
|
||||
if err := c.Prepare(testContext(t)); len(err) > 0 {
|
||||
t.Fatalf("bad: %#v", err)
|
||||
}
|
||||
|
||||
if c.SSHBastionCertificateFile != "" {
|
||||
t.Fatalf("Identity certificate somehow set")
|
||||
}
|
||||
|
||||
if c.SSHPrivateKeyFile != "" {
|
||||
t.Fatalf("Private key file somehow set")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSSHConfigFunc(t *testing.T) {
|
||||
state := new(multistep.BasicStateBag)
|
||||
|
||||
|
@ -170,7 +194,9 @@ func TestSSHConfigFunc(t *testing.T) {
|
|||
if sshConfig.Config.Ciphers[0] != "partycipher" {
|
||||
t.Fatalf("ssh_ciphers should be a direct passthrough.")
|
||||
}
|
||||
|
||||
if c.SSHCertificateFile != "" {
|
||||
t.Fatalf("Identity certificate somehow set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_winrm(t *testing.T) {
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
|
@ -12,6 +11,8 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
|
||||
"github.com/hashicorp/packer/communicator/ssh"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
helperssh "github.com/hashicorp/packer/helper/ssh"
|
||||
|
@ -277,12 +278,23 @@ func sshBastionConfig(config *Config) (*gossh.ClientConfig, error) {
|
|||
"Error expanding path for SSH bastion private key: %s", err)
|
||||
}
|
||||
|
||||
signer, err := helperssh.FileSigner(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if config.SSHBastionCertificateFile != "" {
|
||||
identityPath, err := packer.ExpandUser(config.SSHBastionCertificateFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error expanding path for SSH bastion identity certificate: %s", err)
|
||||
}
|
||||
signer, err := helperssh.FileSignerWithCert(path, identityPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
auth = append(auth, gossh.PublicKeys(signer))
|
||||
} else {
|
||||
signer, err := helperssh.FileSigner(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
auth = append(auth, gossh.PublicKeys(signer))
|
||||
}
|
||||
|
||||
auth = append(auth, gossh.PublicKeys(signer))
|
||||
}
|
||||
|
||||
if config.SSHBastionAgentAuth {
|
||||
|
|
|
@ -5,12 +5,12 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// FileSigner returns an ssh.Signer for a key file.
|
||||
func FileSigner(path string) (ssh.Signer, error) {
|
||||
func parseKeyFile(path string) ([]byte, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -34,6 +34,16 @@ func FileSigner(path string) (ssh.Signer, error) {
|
|||
"Failed to read key '%s': password protected keys are\n"+
|
||||
"not supported. Please decrypt the key prior to use.", path)
|
||||
}
|
||||
return keyBytes, nil
|
||||
}
|
||||
|
||||
// FileSigner returns an ssh.Signer for a key file.
|
||||
func FileSigner(path string) (ssh.Signer, error) {
|
||||
|
||||
keyBytes, err := parseKeyFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error setting up SSH config: %s", err)
|
||||
}
|
||||
|
||||
signer, err := ssh.ParsePrivateKey(keyBytes)
|
||||
if err != nil {
|
||||
|
@ -42,3 +52,64 @@ func FileSigner(path string) (ssh.Signer, error) {
|
|||
|
||||
return signer, nil
|
||||
}
|
||||
|
||||
func ReadCertificate(certificatePath string, keySigner ssh.Signer) (ssh.Signer, error) {
|
||||
|
||||
if certificatePath == "" {
|
||||
return keySigner, fmt.Errorf("no certificate file provided")
|
||||
}
|
||||
|
||||
// Load the certificate
|
||||
cert, err := ioutil.ReadFile(certificatePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read certificate file: %v", err)
|
||||
}
|
||||
|
||||
pk, _, _, _, err := ssh.ParseAuthorizedKey(cert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse public key: %v", err)
|
||||
}
|
||||
|
||||
certificate, ok := pk.(*ssh.Certificate)
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Error loading certificate")
|
||||
}
|
||||
|
||||
err = checkValidCert(certificate)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s not a valid cert: %v", certificatePath, err)
|
||||
}
|
||||
|
||||
certSigner, err := ssh.NewCertSigner(certificate, keySigner)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create cert signer: %v", err)
|
||||
}
|
||||
|
||||
return certSigner, nil
|
||||
}
|
||||
|
||||
// FileSigner returns an ssh.Signer for a key file.
|
||||
func FileSignerWithCert(path string, certificatePath string) (ssh.Signer, error) {
|
||||
|
||||
keySigner, err := FileSigner(path)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ReadCertificate(certificatePath, keySigner)
|
||||
}
|
||||
|
||||
func checkValidCert(cert *ssh.Certificate) error {
|
||||
const CertTimeInfinity = 1<<64 - 1
|
||||
unixNow := time.Now().Unix()
|
||||
|
||||
if after := int64(cert.ValidAfter); after < 0 || unixNow < int64(cert.ValidAfter) {
|
||||
return fmt.Errorf("ssh: cert is not yet valid")
|
||||
}
|
||||
if before := int64(cert.ValidBefore); cert.ValidBefore != uint64(CertTimeInfinity) && (unixNow >= before || before < 0) {
|
||||
return fmt.Errorf("ssh: cert has expired")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue