Skip to content

Commit

Permalink
Refactor and improve logic for signing
Browse files Browse the repository at this point in the history
  • Loading branch information
hslatman committed Jul 28, 2021
1 parent b2198bf commit aedfcf8
Show file tree
Hide file tree
Showing 13 changed files with 284 additions and 197 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ The access control policies described in a MUD File allow network controllers to
## Things that can be done

* Fix (most) TODOs ... :-)
* Replace calls to fmt with proper logging
* Add 'Use' to commands
* Replace calls to fmt with proper logging / output
* Allow the tool to be chained (i.e. use STDIN/STDOUT, pipes, etc.)
* A command for generating MUD files (from pcap or some different way)
* A command for editing MUD files (i.e. metadata)
* A command that initializes a .mud directory inside user HOME, that is used for intermediate storage? If necessary, of course.
Expand Down
39 changes: 10 additions & 29 deletions cmd/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,51 +18,32 @@ package cmd
import (
"fmt"

"github.com/openconfig/ygot/ygot"
"github.com/pkg/errors"
"github.com/spf13/cobra"

"github.com/hslatman/mud-cli/internal"
"github.com/hslatman/mud.yang.go/pkg/mudyang"
)

// readCmd represents the read command
var readCmd = &cobra.Command{
Use: "read",
Short: "A brief description of your command",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Args: cobra.MinimumNArgs(1),
Short: "Reads and prints MUD file contents",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
json, err := internal.Contents(args[0])
if err != nil {
return errors.Wrap(err, "error reading file contents")
}

mud := &mudyang.Mudfile{}
if err := mudyang.Unmarshal(json, mud); err != nil {
return errors.Wrap(err, "can't unmarshal JSON")
filepath := args[0]
mudfile, err := internal.ReadMUDFileFrom(filepath)
if err != nil {
return errors.Wrap(err, "could not get contents")
}

// TODO: print a summary instead of the full JSON?

jsonString, err := ygot.EmitJSON(mud, &ygot.EmitJSONConfig{
Format: ygot.RFC7951,
Indent: " ",
RFC7951Config: &ygot.RFC7951JSONConfig{
AppendModuleName: true,
},
SkipValidation: false, // TODO: provide flag to skip?
})
json, err := internal.JSON(mudfile)
if err != nil {
return errors.Wrap(err, "could not marshal MUD file into JSON")
return errors.Wrap(err, "getting JSON representation of MUD file failed")
}

fmt.Println(jsonString)
// TODO: provide ways to show different info? Like a summary?
fmt.Println(json)

return nil
},
Expand Down
14 changes: 2 additions & 12 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ limitations under the License.
package cmd

import (
"fmt"
"os"
"path/filepath"

Expand All @@ -30,16 +29,7 @@ var mudRootDir string
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "mud",
Short: "A brief description of your application",
Long: `A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
// Uncomment the following line if your bare application
// has an action associated with it:
// Run: func(cmd *cobra.Command, args []string) { },
Short: "mud provides several utilities for working with MUD files",
}

// Execute adds all child commands to the root command and sets flags appropriately.
Expand Down Expand Up @@ -70,7 +60,7 @@ func initConfig() {
cobra.CheckErr(err)

mudRootDir = filepath.Join(home, mudDir)
fmt.Println(mudRootDir)
//fmt.Println(mudRootDir)
if !dirExists(mudRootDir) {
err = os.MkdirAll(mudRootDir, 0700) // TODO: right permissions?
cobra.CheckErr(err)
Expand Down
164 changes: 113 additions & 51 deletions cmd/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ import (
"io/ioutil"
"log"
"math/big"
"net/url"
fp "path/filepath"
"strings"
"time"

cms "github.com/github/ietf-cms"
Expand All @@ -39,82 +41,122 @@ import (
"go.step.sm/crypto/pemutil"
)

var baseURLFlag string
var ignoreExistingSignatureFlag bool

// signCmd represents the sign command
var signCmd = &cobra.Command{
Use: "sign",
Short: "A brief description of your command",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Args: cobra.MinimumNArgs(1),
Short: "Signs a MUD file",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {

filepath := args[0]
json, err := internal.Contents(filepath)
data, err := internal.Read(filepath)
if err != nil {
return errors.Wrapf(err, "retrieving contents from %s failed", filepath)
return errors.Wrapf(err, "error reading contents of %s", filepath)
}

mudfile := &mudyang.Mudfile{}
if err := mudyang.Unmarshal(json, mudfile); err != nil {
return errors.Wrapf(err, "unmarshaling JSON failed")
mudfile, err := internal.Parse(data)
if err != nil {
return errors.Wrap(err, "could not get contents")
}

// TODO: provide flag to override an existing signature
// if mudHasSignature(mud) {
// fmt.Printf("this MUD already has a signature available at: %s\n", *mud.Mud.MudSignature)
// return
// TODO: provide option to first emit standard MUD JSON and use the JSON
// representation of that as the material to be signed?

// TODO: look into this logic: if a signature path is know, the signature may already
// exist or not yet. If it does already exist, we may want to re-sign the MUD file.
// The location of the new signature can be assumed to be the same in the end.
// if !ignoreExistingSignatureFlag && mudHasSignature(mudfile) {
// return fmt.Errorf("this MUD already has a signature available at: %s", *mudfile.Mud.MudSignature)
// }

signaturePath, err := internal.SignaturePath(filepath)
fmt.Println("signature path: ", signaturePath)
existingMudUrl, err := internal.MUDURL(mudfile)
fmt.Println("existing mud url: ", existingMudUrl)
if err != nil {
return errors.Wrap(err, "retrieving MUD URL from MUD failed")
}

existingMudSignatureUrl, err := internal.MUDSignatureURL(mudfile)
fmt.Println("existing mud signature: ", existingMudSignatureUrl)
if err != nil {
return errors.Wrap(err, "retrieving MUD signature URL from MUD failed")
}

signatureFilename, err := internal.SignatureFilename(filepath)
fmt.Println("signature path: ", signatureFilename)
if err != nil {
return errors.Wrap(err, "retrieving signature path from MUD failed")
}

newMudURL := existingMudUrl
fmt.Println("new MUD url: ", newMudURL)
newSignatureURL := internal.NewMUDSignatureURL(existingMudUrl, signatureFilename)
fmt.Println("new signature url: ", newSignatureURL)

if baseURLFlag != "" {
newMudURL, err = rewriteBase(newMudURL, baseURLFlag)
if err != nil {
return errors.Wrap(err, "rewriting base URL for MUD URL failed")
}
newSignatureURL, err = rewriteBase(newSignatureURL, baseURLFlag)
if err != nil {
return errors.Wrap(err, "rewriting base URL for MUD signature URL failed")
}
}

fmt.Println("new MUD url: ", newMudURL)
fmt.Println("new signature url: ", newSignatureURL)

//var signatureURL *url.URL
// if baseURLFlag != "" {
// baseURL, err := url.Parse(baseURLFlag)
// if err != nil {
// return errors.Wrap(err, "failed parsing base URL")
// }
// signatureURL = baseURL
// signatureURL.Path = signaturePath // TODO: support path with multiple segments
// } else {
// signatureURL = mudURL
// signatureURL.Path = signaturePath // TODO: support path with multiple segments
// }

// TODO: update Mudfile with location for signature? Needs to be clear that it has indeed be changed.
// TODO: if signing a local file, provide argument for the full path or directory for the signature file, so
// that the right value can be added to the MUD file before signing.

output, err := ygot.DeepCopy(mudfile)
copy, err := ygot.DeepCopy(mudfile)
if err != nil {
return errors.Wrap(err, "creating deep copy of MUD YANG representation failed")
}

outputMudfile, ok := output.(*mudyang.Mudfile)
copyMUDFile, ok := copy.(*mudyang.Mudfile)
if !ok {
return errors.New("the output MUD YANG is not a *mudyang.Mudfile")
}

// TODO: if changing the signature, should we update the updated_at too? And/or others?
outputMudfile.Mud.MudSignature = &signaturePath
// TODO: change other properties?
mudURLString := newMudURL.String()
copyMUDFile.Mud.MudUrl = &mudURLString
signatureURLString := newSignatureURL.String()
copyMUDFile.Mud.MudSignature = &signatureURLString

n, err := ygot.Diff(mudfile, outputMudfile)
diff, err := ygot.Diff(mudfile, copyMUDFile)
if err != nil {
return errors.Wrap(err, "diffing the input and output MUD file failed")
}

// TODO: after diffing, check only expected number of changes were made
log.Println("diff: ", n)

//jsonString, err := ygot.EmitJSON(outputMudfile, &ygot.EmitJSONConfig{
jsonString, err := ygot.EmitJSON(mudfile, &ygot.EmitJSONConfig{
Format: ygot.RFC7951,
Indent: " ",
RFC7951Config: &ygot.RFC7951JSONConfig{
AppendModuleName: true,
},
SkipValidation: false,
// TODO: other validation options?
})
if err != nil {
return errors.Wrap(err, "could not marshal MUD file into JSON")
}
// TODO: can the diff be printed nicely (easily)? It seems to be some text values ...
log.Println("diff: ", diff)

data := []byte(jsonString)
differencesFound := len(diff.GetDelete())+len(diff.GetUpdate()) > 0
if differencesFound {
// TODO: in case we're using the copy, we probably also need to update the updated_at
json, err := internal.JSON(copyMUDFile)
if err != nil {
return errors.Wrap(err, "getting JSON representation of MUD file failed")
}
data = []byte(json)
}

fmt.Println(data)

Expand Down Expand Up @@ -180,6 +222,9 @@ to quickly create a Cobra application.`,
return errors.Wrap(err, "writing DER signature failed")
}

// TODO: if differences were found, we also should store the new
// MUD file, so that it can be uploaded later.

fmt.Println(signature)

// TODO: print output on how/where to store the file + signature?
Expand Down Expand Up @@ -237,16 +282,33 @@ func mudHasSignature(mud *mudyang.Mudfile) bool {
return mud.Mud.MudSignature != nil
}

func rewriteBase(u *url.URL, baseURLString string) (*url.URL, error) {
// TODO: check this works as expected
base, err := url.Parse(baseURLString)
if err != nil {
return nil, err
}
path := u.EscapedPath()
filename := fp.Base(path)
baseString := base.String()
if !strings.HasSuffix(baseString, "/") {
baseString = baseString + "/"
}
newURL, err := url.Parse(baseString + filename)
if err != nil {
return nil, err
}
newURL.RawQuery = u.RawQuery
newURL.Fragment = u.Fragment
return newURL, nil
}

func init() {
rootCmd.AddCommand(signCmd)

// Here you will define your flags and configuration settings.

// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// signCmd.PersistentFlags().String("foo", "", "A help for foo")
// TODO: provide a flag that uses a base URL for the MUD URL and signature to exist in the file?

// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// signCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
signCmd.PersistentFlags().StringVarP(&baseURLFlag, "base-url", "u", "", "Base URL to use for MUD URL and signature location")
signCmd.PersistentFlags().StringVarP(&signatureFlag, "signature", "s", "", "Location of signature file to set")
signCmd.PersistentFlags().BoolVar(&ignoreExistingSignatureFlag, "ignore-existing-signature", false, "Ignore")
}
Loading

0 comments on commit aedfcf8

Please sign in to comment.