* sshrimp-agent and sshrimp-ca building and deploying. * mage build system working. * successful deploy and SSH to host. * need to tidy up and add tests.
93 lines
3.0 KiB
Go
93 lines
3.0 KiB
Go
package signer
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
"github.com/aws/aws-sdk-go/aws/session"
|
|
"github.com/aws/aws-sdk-go/service/lambda"
|
|
"github.com/pkg/errors"
|
|
"github.com/stoggi/sshrimp/internal/config"
|
|
"golang.org/x/crypto/ssh"
|
|
)
|
|
|
|
// SSHrimpResult encodes the payload format returned from the sshrimp-ca lambda
|
|
type SSHrimpResult struct {
|
|
Certificate string `json:"certificate"`
|
|
ErrorMessage string `json:"errorMessage"`
|
|
ErrorType string `json:"errorType"`
|
|
}
|
|
|
|
// SSHrimpEvent encodes the user input for the sshrimp-ca lambda
|
|
type SSHrimpEvent struct {
|
|
PublicKey string `json:"publickey"`
|
|
Token string `json:"token"`
|
|
SourceAddress string `json:"sourceaddress"`
|
|
ForceCommand string `json:"forcecommand"`
|
|
}
|
|
|
|
// SignCertificateAllRegions iterate through each configured region if there is an error signing the certificate
|
|
func SignCertificateAllRegions(publicKey ssh.PublicKey, token string, forceCommand string, c *config.SSHrimp) (*ssh.Certificate, error) {
|
|
var err error
|
|
|
|
// Try each configured region before exiting if there is an error
|
|
for _, region := range c.CertificateAuthority.Regions {
|
|
cert, err := SignCertificateOneRegion(publicKey, token, forceCommand, region, c)
|
|
if err == nil {
|
|
return cert, nil
|
|
}
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
// SignCertificateOneRegion given a public key, identity token and forceCommand, invoke the sshrimp-ca lambda function
|
|
func SignCertificateOneRegion(publicKey ssh.PublicKey, token string, forceCommand string, region string, c *config.SSHrimp) (*ssh.Certificate, error) {
|
|
// Create a lambdaService using the new temporary credentials for the role
|
|
session := session.Must(session.NewSession(&aws.Config{
|
|
Region: aws.String(region),
|
|
}))
|
|
lambdaService := lambda.New(session)
|
|
|
|
// Setup the JSON payload for the SSHrimp CA
|
|
payload, err := json.Marshal(SSHrimpEvent{
|
|
PublicKey: string(ssh.MarshalAuthorizedKey(publicKey)),
|
|
Token: token,
|
|
ForceCommand: forceCommand,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Invoke the SSHrimp lambda
|
|
result, err := lambdaService.Invoke(&lambda.InvokeInput{
|
|
FunctionName: aws.String(c.CertificateAuthority.FunctionName),
|
|
Payload: payload,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if *result.StatusCode != 200 {
|
|
return nil, fmt.Errorf("sshrimp returned status code %d", *result.StatusCode)
|
|
}
|
|
|
|
// Parse the result form the lambda to extract the certificate
|
|
sshrimpResult := SSHrimpResult{}
|
|
err = json.Unmarshal(result.Payload, &sshrimpResult)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to parse json response from sshrimp-ca")
|
|
}
|
|
|
|
// These error types and messages can also come from the aws-sdk-go lambda handler
|
|
if sshrimpResult.ErrorType != "" || sshrimpResult.ErrorMessage != "" {
|
|
return nil, fmt.Errorf("%s: %s", sshrimpResult.ErrorType, sshrimpResult.ErrorMessage)
|
|
}
|
|
|
|
// Parse the certificate received by sshrimp-ca
|
|
cert, _, _, _, err := ssh.ParseAuthorizedKey([]byte(sshrimpResult.Certificate))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return cert.(*ssh.Certificate), nil
|
|
}
|