Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# This is an example .goreleaser.yml file with some sane defaults.
# Make sure to check the documentation at http://goreleaser.com
before:
hooks:
# You may remove this if you don't use go modules.
- go mod tidy
# you may remove this if you don't need go generate
- go generate ./...
builds:
- env:
goos:
- linux
archives:
- replacements:
linux: Linux
386: i386
amd64: x86_64
files:
- README.md
- LICENSE
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ .Tag }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'

signs:
- id: signify
signature: ${artifact}.sig
cmd: signify
args: ["-S", "-s", "{{ .Env.HOME }}/.signify/ssh-sentinel.sec", "-m", "${artifact}", "-x", "${artifact}.sig"]
artifacts: checksum
release:
prerelease: auto
mode: keep-existing
17 changes: 17 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
test:
go test ./...

test_coverage:
go test ./... -coverprofile .testCoverage.txt

doc:
godoc -http=:6060

release:
$(shell goreleaser release --rm-dist)

release-dry:
$(shell goreleaser release --skip-publish)

snapshot:
$(shell goreleaser release --snapshot --skip-publish --rm-dist)
38 changes: 38 additions & 0 deletions cmd/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package cmd

import (
"encoding/json"
"github.com/spf13/cobra"
"github.com/st2projects/ssh-sentinel-client/config"
"os"
"path/filepath"
)

var initCmd = &cobra.Command{
Use: "init",
Short: "Initialise the client",
Run: func(cmd *cobra.Command, args []string) {

configBytes, err := json.MarshalIndent(&config.ConfigType{}, "", " ")

if err != nil {
panic(err)
}

userHome, err := os.UserHomeDir()

if err != nil {
panic(err)
}

configPath := filepath.Join(userHome, ".ssh-sentinel.json")
err = os.WriteFile(configPath, configBytes, os.FileMode(0600))
if err != nil {
panic(err)
}
},
}

func init() {
rootCmd.AddCommand(initCmd)
}
19 changes: 19 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package cmd

import (
"github.com/spf13/cobra"
"os"
)

var rootCmd = &cobra.Command{
Use: "ssh-sentinel-client <ARGS>",
Short: "A simple ssh-sentinel client",
}

func Execute() {
err := rootCmd.Execute()

if err != nil {
os.Exit(1)
}
}
91 changes: 91 additions & 0 deletions cmd/sign.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package cmd

import (
"bytes"
"encoding/json"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/st2projects/ssh-sentinel-client/config"
"github.com/st2projects/ssh-sentinel-client/helpers"
"github.com/st2projects/ssh-sentinel-core/model"
"io"
"net/http"
"os"
)

var configPath string

var signCmd = &cobra.Command{
Use: "sign",
Short: "Sign a new key",
Run: func(cmd *cobra.Command, args []string) {
config.MakeConfig(configPath)

conf := config.Config

certValid, expDate := helpers.IsCertValid(conf.GetCertFile())
if certValid {
log.Infof("Existing cert valid until %s", expDate)
} else {
log.Info("Creating new cert")
signNewKey(conf)
}
},
}

func signNewKey(conf *config.ConfigType) {
pubKey := conf.GetPublicKey()

if !helpers.PathExists(pubKey) {
panic("Key " + conf.PublicKey + " does not exist")
}

key, err := os.ReadFile(pubKey)

if err != nil {
panic(err)
}

signReq := &model.KeySignRequest{
Username: conf.Username,
APIKey: conf.APIKey,
Principals: conf.Principals,
Key: string(key),
}

signReqBytes, err := json.Marshal(signReq)

if err != nil {
panic(err)
}

resp, err := http.Post(conf.EndPoint, "application/json", bytes.NewBuffer(signReqBytes))

body, err := io.ReadAll(resp.Body)

if err != nil {
panic(err)
}

signResp := &model.KeySignResponse{}

json.Unmarshal(body, signResp)

if !signResp.Success {
log.Errorf("Sign request failed with err: %s", signResp.Message)
} else {
err := os.WriteFile(helpers.ExpandPath(conf.GetCertFile()), []byte(signResp.SignedKey), os.FileMode(0600))
if err != nil {
log.Errorf("Failed to write new cert %s", err.Error())
} else {
log.Info("Signed new key")
}
}
}

func init() {
rootCmd.AddCommand(signCmd)
signCmd.Flags().StringVarP(&configPath, "config", "c", "", "Config file")

signCmd.MarkFlagRequired("config")
}
43 changes: 43 additions & 0 deletions config/configReader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package config

import (
"encoding/json"
"github.com/st2projects/ssh-sentinel-client/helpers"
"os"
)

type ConfigType struct {
EndPoint string `json:"endPoint"`
APIKey string `json:"apiKey"`
Username string `json:"username"`
Principals []string `json:"principals"`
PublicKey string `json:"publicKey"`
CertFile string `json:"certFile"`
}

var Config *ConfigType

func MakeConfig(configFile string) {
if !helpers.PathExists(configFile) {
panic("config file " + configFile + " does not exits")
}

configString, err := os.ReadFile(configFile)
if err != nil {
panic(err)
}

err = json.Unmarshal(configString, &Config)
if err != nil {
panic(err)
}

}

func (c *ConfigType) GetPublicKey() string {
return helpers.ExpandPath(c.PublicKey)
}

func (c *ConfigType) GetCertFile() string {
return helpers.ExpandPath(c.CertFile)
}
13 changes: 10 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@ module github.com/st2projects/ssh-sentinel-client
go 1.18

require (
github.com/st2projects/ssh-sentinel-core v1.0.0
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.5.0
)
github.com/spf13/cobra v1.5.0
github.com/st2projects/ssh-sentinel-core v1.0.0
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d
)

require (
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
)
29 changes: 29 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/st2projects/ssh-sentinel-core v1.0.0 h1:9MKquOBeExd660PWkJ221pIa6qw11Bvec9TfA90W1os=
github.com/st2projects/ssh-sentinel-core v1.0.0/go.mod h1:x7Lj7JO1u4BT0iWnj66eIbn9fqQD1qB2ollTFsVzzHg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
47 changes: 47 additions & 0 deletions helpers/keyHelper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package helpers

import (
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh"
"os"
"time"
)

func IsCertValid(certPath string) (bool, string) {

certValid := PathExists(certPath)

if certValid {
certBytes, err := os.ReadFile(certPath)

if err != nil {
log.Errorf("%s - cert does not exist or cannot be read", certPath)
}

pub, _, _, _, err := ssh.ParseAuthorizedKey(certBytes)

if err != nil {
log.Errorf("Error when parsing cert: %s", err.Error())
}

cert, ok := pub.(*ssh.Certificate)

if !ok {
log.Errorf("Failed to cast to cert")
}

now := time.Now().UTC()
validBefore := time.Unix(int64(cert.ValidBefore), 0).UTC()
validAfter := time.Unix(int64(cert.ValidAfter), 0).UTC()

validBeforeString := validBefore.Format("2006-01-01 15:04:05.5 -0700")

if now.After(validAfter) && now.Before(validBefore) {
return true, validBeforeString
} else {
return false, validBeforeString
}
}

return false, "Cert not found"
}
28 changes: 28 additions & 0 deletions helpers/pathHelper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package helpers

import (
"errors"
"os"
"os/user"
"path/filepath"
"strings"
)

func ExpandPath(path string) string {
usr, _ := user.Current()
homeDir := usr.HomeDir

if path == "~" {
path = homeDir
} else if strings.HasPrefix(path, "~/") {
path = filepath.Join(homeDir, path[2:])
}

return path
}

func PathExists(path string) bool {
_, err := os.Stat(path)

return !(err != nil && errors.Is(err, os.ErrNotExist))
}
7 changes: 7 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package main

import "github.com/st2projects/ssh-sentinel-client/cmd"

func main() {
cmd.Execute()
}