grim/hgkeeper

access:
access-control
2019-04-28, Wagner Riffel
bdb2c82e0679
Parents 8c7e5a1b48ed
Children c8e556033bea
access:
permissions: moved to its own file
yaml decoder: rolled back to old interface of taking an io.Reader
decoder: now it reads multiple keys from authrorized_keys file
all: minor syntaxy issues fixed, removed unsused variables
--- a/access/access.go Sat Apr 27 18:19:37 2019 -0300
+++ b/access/access.go Sun Apr 28 02:00:26 2019 -0300
@@ -1,10 +1,11 @@
package access
import (
+ "bufio"
"bytes"
"fmt"
"io"
- "io/ioutil"
+ "os"
"path/filepath"
"sync"
@@ -21,11 +22,6 @@
KeysDir = "keys"
)
-var (
- keysCacheMu sync.RWMutex
- keysCache = make(map[string]string)
-)
-
const (
// Public is a reserved hgkeeper name and is not valid in the patterns
// or keys of an access file.
@@ -41,38 +37,6 @@
reposDir string
)
-// A Perm represents one or more access permissions: reading, writing, etc.
-type Perm uint32
-
-// All permisions constants
-const (
- Init Perm = 1 << iota
- Read
- Write
-)
-
-// String returns a textual representation of the perm.
-func (p Perm) String() string {
- var r string
- if p.can(Init) {
- r += "init|"
- }
- if p.can(Read) {
- r += "read|"
- }
- if p.can(Write) {
- r += "write|"
- }
- if len(r) == 0 {
- return "none"
- }
- return r[:len(r)-1]
-}
-
-func (p *Perm) can(i Perm) bool { return *p&i != 0 }
-func (p *Perm) clear(i Perm) { *p &^= i }
-func (p *Perm) set(i Perm) { *p |= i }
-
type _key struct {
fprint string
p Perm
@@ -90,23 +54,14 @@
// global []_key
}
-// parse parses access yml into a
-func (a *Access) parse(b []byte) error {
- dec := yaml.NewDecoder(bytes.NewReader(b))
- for {
- err := dec.Decode(a)
- if err != nil {
- if err == io.EOF {
- break
- }
- return err
- }
- }
- return nil
+// parse calls inderectly UnmarshalYAML, it's caller's duty to control the access to a.
+func (a *Access) parse(r io.Reader) error {
+ return yaml.NewDecoder(r).Decode(a)
}
// UnmarshalYAML unmarshals access yaml into an internal access
-// representation
+// representation.
+// It's caller's duty to control the access to a.
func (a *Access) UnmarshalYAML(f func(interface{}) error) error {
type acl struct {
Init []string `yaml:"init"`
@@ -129,13 +84,12 @@
log.Errorf("access: %q group is reserved, ignored", i)
continue
}
- _, exists := groups[i]
- if exists {
+ if _, exists := groups[i]; exists {
log.Errorf("access: group %q registered twice", i)
continue
}
- for _, key := range v {
- k, err := loadKey(key)
+ for _, keyFile := range v {
+ k, err := loadKey(keyFile)
if err != nil {
log.Errorf("%v", err)
if _, ok := groups[i]; !ok {
@@ -145,7 +99,11 @@
}
continue
}
- groups[i] = append(groups[i], _key{fprint: k})
+ keys := make([]_key, 0, len(k))
+ for _, v := range k {
+ keys = append(keys, _key{fprint: v})
+ }
+ groups[i] = append(groups[i], keys...)
}
}
@@ -162,23 +120,20 @@
a.setPathWrite(path, v.Write, groups)
}
}
- // what to do with dummy.Global?
- fmt.Printf("%+v\n", a.paths)
return nil
}
-func (a *Access) addToPath(path string, k _key, p Perm) {
- a.pathsMu.Lock()
- defer a.pathsMu.Unlock()
-
- if keys, found := a.paths[path]; found {
- if n := k.find(keys); n >= 0 {
- a.paths[path][n].p.set(p)
- return
+func (a *Access) addToPath(path string, p Perm, ks ..._key) {
+ for _, k := range ks {
+ if keys, found := a.paths[path]; found {
+ if n := k.find(keys); n >= 0 {
+ a.paths[path][n].p.set(p)
+ return
+ }
}
+ k.p.set(p)
+ a.paths[path] = append(a.paths[path], k)
}
- k.p.set(p)
- a.paths[path] = append(a.paths[path], k)
}
func (a *Access) setPathInit(path string, init []string, groups map[string][]_key) {
@@ -188,17 +143,20 @@
for i := range groups[key] {
k := groups[key][i]
- a.addToPath(path, k, Init)
+ a.addToPath(path, Init, k)
}
continue
}
- fp, err := loadKey(key)
+ ks, err := loadKey(key)
if err != nil {
log.Errorf("%v", err)
continue
}
- k := _key{fprint: fp}
- a.addToPath(path, k, Init)
+ keys := make([]_key, 0, len(ks))
+ for _, v := range ks {
+ keys = append(keys, _key{fprint: v})
+ }
+ a.addToPath(path, Init, keys...)
}
}
@@ -209,17 +167,20 @@
for i := range groups[key] {
k := groups[key][i]
- a.addToPath(path, k, Read)
+ a.addToPath(path, Read, k)
}
continue
}
- fp, err := loadKey(key)
+ ks, err := loadKey(key)
if err != nil {
log.Errorf("%v", err)
continue
}
- k := _key{fprint: fp}
- a.addToPath(path, k, Read)
+ keys := make([]_key, 0, len(ks))
+ for _, v := range ks {
+ keys = append(keys, _key{fprint: v})
+ }
+ a.addToPath(path, Read, keys...)
}
}
@@ -230,17 +191,20 @@
for i := range groups[key] {
k := groups[key][i]
- a.addToPath(path, k, Write)
+ a.addToPath(path, Write, k)
}
continue
}
- fp, err := loadKey(key)
+ ks, err := loadKey(key)
if err != nil {
log.Errorf("%v", err)
continue
}
- k := _key{fprint: fp}
- a.addToPath(path, k, Write)
+ keys := make([]_key, 0, len(ks))
+ for _, v := range ks {
+ keys = append(keys, _key{fprint: v})
+ }
+ a.addToPath(path, Write, keys...)
}
}
@@ -264,18 +228,16 @@
// from disk
func (a *Access) Reset() error {
a.pathsMu.Lock()
+ defer a.pathsMu.Unlock()
for i := range a.paths {
delete(a.paths, i)
}
- a.pathsMu.Unlock()
- b, err := ioutil.ReadFile(accessFile)
+ f, err := os.Open(accessFile)
if err != nil {
return err
}
- if err = a.parse(b); err != nil {
- return err
- }
- return nil
+ defer f.Close()
+ return a.parse(f)
}
// New initializes access strucuture, if any error is
@@ -285,34 +247,37 @@
reposDir = reposPath
accessFile = filepath.Join(reposPath, "hgkeeper", AccessFile)
keysDir = filepath.Join(reposPath, "hgkeeper", KeysDir)
- b, err := ioutil.ReadFile(accessFile)
- if err != nil {
- return nil, err
- }
a := new(Access)
- a.pathsMu.Lock()
a.paths = make(map[string][]_key)
- a.pathsMu.Unlock()
-
- if err = a.parse(b); err != nil {
- return nil, err
- }
- return a, nil
+ return a, a.Reset()
}
// loadKey reads repoPath/hgkeeper/keys/name and tries to
// parse as an authorized keys format, and returns its fingerprint
-func loadKey(name string) (string, error) {
- k, err := ioutil.ReadFile(filepath.Join(keysDir, name))
+func loadKey(name string) ([]string, error) {
+ f, err := os.Open(filepath.Join(keysDir, name))
if err != nil {
- return "", fmt.Errorf("loadKey %q: %v", name, err)
+ return nil, fmt.Errorf("loadKey %q: %v", name, err)
}
- //
- pub, _, _, _, err := ssh.ParseAuthorizedKey(k)
- if err != nil {
- return "", fmt.Errorf("loadKey %q: %v", name, err)
+ defer f.Close()
+ keys := make([]string, 0, 20)
+ // according to sshd(8) each line of the file contains one key,
+ // ignoring empty and lines starting with #
+ bio := bufio.NewScanner(f)
+ for line := 1; bio.Scan(); line++ {
+ key := bio.Text()
+ if key == "" || key[0] == '#' {
+ continue
+ }
+ pub, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key))
+ if err != nil {
+ log.Errorf("loadKey %q:%d: %v", name, line, err)
+ continue
+ }
+ keys = append(keys, ssh.FingerprintSHA256(pub))
}
- return ssh.FingerprintSHA256(pub), nil
+
+ return keys, nil
}
// isPublic checks whenever u is or not the reserved
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/access/permissions.go Sun Apr 28 02:00:26 2019 -0300
@@ -0,0 +1,33 @@
+package access
+
+// A Perm represents one or more access permissions: reading, writing, etc.
+type Perm uint32
+
+// All permisions constants
+const (
+ Init Perm = 1 << iota
+ Read
+ Write
+)
+
+// String returns a textual representation of the perm.
+func (p Perm) String() string {
+ var r string
+ if p.can(Init) {
+ r += "init|"
+ }
+ if p.can(Read) {
+ r += "read|"
+ }
+ if p.can(Write) {
+ r += "write|"
+ }
+ if len(r) == 0 {
+ return "none"
+ }
+ return r[:len(r)-1]
+}
+
+func (p *Perm) can(i Perm) bool { return *p&i != 0 }
+func (p *Perm) clear(i Perm) { *p &^= i }
+func (p *Perm) set(i Perm) { *p |= i }
--- a/ssh/server.go Sat Apr 27 18:19:37 2019 -0300
+++ b/ssh/server.go Sun Apr 28 02:00:26 2019 -0300
@@ -63,9 +63,7 @@
}
func (s *Server) publicKeyCallback(meta ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
- if !s.a.Can("", "", access.Read) {
- return nil, fmt.Errorf("permission denied")
- }
+ log.Debugf("key: %s\n", key.Marshal())
return nil, nil
}