From bacae877da062a6001157ef13d8fb57d835dd71d Mon Sep 17 00:00:00 2001 From: Thomas Miller Date: Wed, 26 Sep 2018 10:24:55 +1000 Subject: [PATCH] Adds support for mode, uid & gid --- README.md | 2 ++ cmd/config/dest.go | 50 +++++++++++++++++++++++++++++------ dest/file_dest.go | 49 +++++++++++++++++++++++----------- file/file.go | 66 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+), 23 deletions(-) create mode 100644 file/file.go diff --git a/README.md b/README.md index 226db05..8414cd7 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,8 @@ dest specific options. Options specific to the dest type choosen. `certBundleFile` - optional location to output issued certificate bundle to `privKeyFile` - optional location to output private key to + + All file options support a Mode, Gid and Uid options appended at the end. Example `privKeyFileMode` `action` - required diff --git a/cmd/config/dest.go b/cmd/config/dest.go index 3e1143c..2b683e9 100644 --- a/cmd/config/dest.go +++ b/cmd/config/dest.go @@ -3,10 +3,13 @@ package config import ( "encoding/json" "fmt" + "os" + "strconv" "github.com/pkg/errors" "github.com/tlmiller/disttrust/dest" + "github.com/tlmiller/disttrust/file" ) func ToDest(id string, opts json.RawMessage) (dest.Dest, error) { @@ -19,17 +22,48 @@ func ToDest(id string, opts json.RawMessage) (dest.Dest, error) { return nil, errors.Wrap(err, "parsing dest json") } fdest := dest.File{} - if cafile, exists := uopts["caFile"]; exists { - fdest.CAFile = cafile + + caFile, err := destBuilder(uopts["caFile"], uopts["caFileMode"], + uopts["caFileGid"], uopts["caFileUid"]) + if err != nil { + return nil, errors.Wrap(err, "caFile") } - if cfile, exists := uopts["certFile"]; exists { - fdest.CertificateFile = cfile + fdest.CA = caFile + + cFile, err := destBuilder(uopts["certFile"], uopts["certFileMode"], + uopts["certFileGid"], uopts["certFileUid"]) + if err != nil { + return nil, errors.Wrap(err, "certFile") } - if cbfile, exists := uopts["certBundleFile"]; exists { - fdest.CertificateBundleFile = cbfile + fdest.Certificate = cFile + + cbFile, err := destBuilder(uopts["certBundleFile"], uopts["certBundleFileMode"], + uopts["certBundleFileGid"], uopts["certBundleFileUid"]) + if err != nil { + return nil, errors.Wrap(err, "certBundleFile") } - if pkfile, exists := uopts["privKeyFile"]; exists { - fdest.PrivateKeyFile = pkfile + fdest.CertificateBundle = cbFile + + pkfile, err := destBuilder(uopts["privKeyFile"], uopts["privKeyFileMode"], + uopts["privKeyFileGid"], uopts["privKeyFileUid"]) + if err != nil { + return nil, errors.Wrap(err, "privKeyFile") } + fdest.PrivateKey = pkfile + return &fdest, nil } + +func destBuilder(path, mode, gid, uid string) (file.File, error) { + builder := file.New(path) + if mode != "" { + conv, err := strconv.ParseUint(mode, 8, 32) + if err != nil { + return file.File{}, errors.Wrap(err, "invalid mode uint") + } + builder.Mode = os.FileMode(conv) + } + builder.Gid = gid + builder.Uid = uid + return builder, nil +} diff --git a/dest/file_dest.go b/dest/file_dest.go index 8971205..21f09f6 100644 --- a/dest/file_dest.go +++ b/dest/file_dest.go @@ -6,50 +6,69 @@ import ( "github.com/pkg/errors" + "github.com/tlmiller/disttrust/file" "github.com/tlmiller/disttrust/provider" ) type File struct { - CAFile string - CertificateFile string - CertificateBundleFile string - PrivateKeyFile string + CA file.File + Certificate file.File + CertificateBundle file.File + PrivateKey file.File } func (f *File) Send(res *provider.Response) error { - if res.CA != "" && f.CAFile != "" { - err := ioutil.WriteFile(f.CAFile, []byte(res.CA), os.FileMode(0644)) + if res.CA != "" && f.CA.HasPath() { + err := ioutil.WriteFile(f.CA.Path, []byte(res.CA), f.CA.Mode) if err != nil { return errors.Wrap(err, "writing ca file") } + err = f.CA.Chown() + if err != nil { + return errors.Wrap(err, "chown ca file") + } } - if res.Certificate != "" && f.CertificateFile != "" { - err := ioutil.WriteFile(f.CertificateFile, []byte(res.Certificate), os.FileMode(0644)) + if res.Certificate != "" && f.Certificate.HasPath() { + err := ioutil.WriteFile(f.Certificate.Path, []byte(res.Certificate), + f.Certificate.Mode) if err != nil { return errors.Wrap(err, "writing certificate file") } + err = f.Certificate.Chown() + if err != nil { + return errors.Wrap(err, "chown certificate file") + } } - if res.CABundle != "" && f.CertificateBundleFile != "" { - f, err := os.OpenFile(f.CertificateBundleFile, - os.O_WRONLY|os.O_TRUNC|os.O_CREATE, os.FileMode(0644)) - defer f.Close() + if res.CABundle != "" && f.CertificateBundle.HasPath() { + s, err := os.OpenFile(f.CertificateBundle.Path, + os.O_WRONLY|os.O_TRUNC|os.O_CREATE, f.CertificateBundle.Mode) + defer s.Close() if err != nil { return errors.Wrap(err, "writing certificate bundle file") } - _, err = f.WriteString(res.CABundle + "\n" + res.Certificate) + _, err = s.WriteString(res.CABundle + "\n" + res.Certificate) if err != nil { return errors.Wrap(err, "writing certificate bundle file") } + err = f.CertificateBundle.Chown() + if err != nil { + return errors.Wrap(err, "chown certificate bundle file") + } } - if res.PrivateKey != "" && f.PrivateKeyFile != "" { - err := ioutil.WriteFile(f.PrivateKeyFile, []byte(res.PrivateKey), os.FileMode(0600)) + if res.PrivateKey != "" && f.PrivateKey.HasPath() { + err := ioutil.WriteFile(f.PrivateKey.Path, []byte(res.PrivateKey), + f.PrivateKey.Mode) if err != nil { return errors.Wrap(err, "writing private key file") } + err = f.PrivateKey.Chown() + if err != nil { + return errors.Wrap(err, "chown private key file") + } } return nil } diff --git a/file/file.go b/file/file.go new file mode 100644 index 0000000..d61c342 --- /dev/null +++ b/file/file.go @@ -0,0 +1,66 @@ +package file + +import ( + "os" + "os/user" + "strconv" + + "github.com/pkg/errors" +) + +type File struct { + Gid string + Path string + Mode os.FileMode + Uid string +} + +func (f File) Chown() error { + var gid, uid int + if f.Gid == "" { + gid = -1 + } else if conv, err := strconv.ParseInt(f.Gid, 10, 32); err == nil { + gid = int(conv) + } else { + group, err := user.LookupGroup(f.Gid) + if err != nil { + return errors.Wrapf(err, "looking up group for id %s", f.Gid) + } + conv, err := strconv.ParseInt(group.Gid, 10, 32) + if err != nil { + return errors.Wrapf(err, "parsing group lookup gid for id %s", group.Gid) + } + gid = int(conv) + } + + if f.Uid == "" { + uid = -1 + } else if conv, err := strconv.ParseInt(f.Uid, 10, 32); err == nil { + uid = int(conv) + } else { + user, err := user.Lookup(f.Uid) + if err != nil { + return errors.Wrapf(err, "looking up user for id %s", f.Uid) + } + conv, err := strconv.ParseInt(user.Uid, 10, 32) + if err != nil { + return errors.Wrapf(err, "parsing user lookup gid for id %s", user.Uid) + } + uid = int(conv) + } + + return os.Chown(f.Path, uid, gid) +} + +func (f File) HasPath() bool { + return f.Path != "" +} + +func New(path string) File { + return File{ + Path: path, + Mode: os.FileMode(0644), + Gid: "", + Uid: "", + } +}