[medium] Integrate ACL verification into Agent action validation code

This commit is contained in:
Julien Vehent 2014-04-06 13:06:28 -04:00
Родитель 8b9e360879
Коммит 9ef088aa17
3 изменённых файлов: 122 добавлений и 26 удалений

86
src/mig/agent/acl.go Normal file
Просмотреть файл

@ -0,0 +1,86 @@
/* Mozilla InvestiGator Agent
Version: MPL 1.1/GPL 2.0/LGPL 2.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
The Initial Developer of the Original Code is
Mozilla Corporation
Portions created by the Initial Developer are Copyright (C) 2014
the Initial Developer. All Rights Reserved.
Contributor(s):
Julien Vehent jvehent@mozilla.com [:ulfr]
Guillaume Destuynder <kang@mozilla.com>
Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
*/
package main
import (
"fmt"
"mig"
"mig/pgp"
"time"
)
// checkActionAuthorization verifies the PGP signatures of a given action
// against the Access Control List of the agent.
func checkActionAuthorization(a mig.Action, ctx Context) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("checkActionAuthorization() -> %v", e)
}
ctx.Channels.Log <- mig.Log{ActionID: a.ID, Desc: "leaving checkActionAuthorization()"}.Debug()
}()
// get an io.Reader from the public pgp key
keyring, keycount, err := pgp.ArmoredPubKeysToKeyring(PUBLICPGPKEYS[0:])
if err != nil {
panic(err)
}
ctx.Channels.Log <- mig.Log{ActionID: a.ID, Desc: fmt.Sprintf("loaded %d keys", keycount)}.Debug()
// Check the action syntax and signature
err = a.Validate()
if err != nil {
desc := fmt.Sprintf("action validation failed: %v", err)
ctx.Channels.Log <- mig.Log{ActionID: a.ID, Desc: desc}.Err()
panic(desc)
}
// Validate() checks that the action hasn't expired, but we need to
// check the start time ourselves
if time.Now().Before(a.ValidFrom) {
ctx.Channels.Log <- mig.Log{ActionID: a.ID, Desc: "action is scheduled for later"}.Err()
panic("Action ValidFrom date is in the future")
}
// check ACLs, includes verifying signatures
err = a.VerifyACL(ctx.ACL, keyring)
if err != nil {
desc := fmt.Sprintf("action ACL verification failed: %v", err)
ctx.Channels.Log <- mig.Log{ActionID: a.ID, Desc: desc}.Err()
panic(desc)
}
ctx.Channels.Log <- mig.Log{ActionID: a.ID, Desc: "ACL verification succeeded."}.Debug()
return
}

Просмотреть файл

@ -47,7 +47,6 @@ import (
"mig"
"mig/modules/connected"
"mig/modules/filechecker"
"mig/pgp"
"os"
"os/exec"
"strings"
@ -245,33 +244,12 @@ func parseCommands(ctx Context, msg []byte) (err error) {
panic(err)
}
// get an io.Reader from the public pgp key
keyring, keycount, err := pgp.ArmoredPubKeysToKeyring(PUBLICPGPKEYS[0:])
// verify the PGP signature of the action, and verify that
// the signer is authorized to perform this action
err = checkActionAuthorization(cmd.Action, ctx)
if err != nil {
panic(err)
}
ctx.Channels.Log <- mig.Log{CommandID: cmd.ID, ActionID: cmd.Action.ID, Desc: fmt.Sprintf("loaded %d keys", keycount)}.Debug()
// Check the action syntax and signature
err = cmd.Action.Validate()
if err != nil {
desc := fmt.Sprintf("action validation failed: %v", err)
ctx.Channels.Log <- mig.Log{CommandID: cmd.ID, ActionID: cmd.Action.ID, Desc: desc}.Err()
panic(desc)
}
err = cmd.Action.VerifySignature(keyring)
if err != nil {
desc := fmt.Sprintf("action signature verification failed: %v", err)
ctx.Channels.Log <- mig.Log{CommandID: cmd.ID, ActionID: cmd.Action.ID, Desc: desc}.Err()
panic(desc)
}
// Expiration is verified by the Validate() call above, but we need
// to verify the ScheduledDate ourselves
if time.Now().Before(cmd.Action.ValidFrom) {
ctx.Channels.Log <- mig.Log{CommandID: cmd.ID, ActionID: cmd.Action.ID, Desc: "action is scheduled for later"}.Err()
panic("ScheduledDateInFuture")
}
// Each operation is ran separately by a module, a channel is created to receive the results from each module
// a goroutine is created to read from the result channel, and when all modules are done, build the response

Просмотреть файл

@ -39,6 +39,7 @@ import (
"crypto/rand"
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"github.com/VividCortex/godaemon"
"github.com/streadway/amqp"
@ -55,7 +56,7 @@ import (
// logs and channels
// Context is intended as a single structure that can be passed around easily.
type Context struct {
OpID uint64 // ID of the current operation, used for tracking
ACL mig.ACL
Agent struct {
Hostname, OS, QueueLoc, UID, BinPath string
}
@ -77,6 +78,7 @@ type Context struct {
Chan *amqp.Channel
Bind mig.Binding
}
OpID uint64 // ID of the current operation, used for tracking
Sleeper time.Duration // timer used when the agent has to sleep for a while
Stats struct {
}
@ -146,6 +148,12 @@ func Init(foreground bool) (ctx Context, err error) {
panic(err)
}
// parse the ACLs
ctx, err = initACL(ctx)
if err != nil {
panic(err)
}
// connect to the message broker
ctx, err = initMQ(ctx)
if err != nil {
@ -277,6 +285,30 @@ func createIDFile(loc string) (id []byte, err error) {
return
}
// parse the permissions from the configuration into an ACL structure
func initACL(orig_ctx Context) (ctx Context, err error) {
ctx = orig_ctx
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("initACL() -> %v", e)
}
ctx.Channels.Log <- mig.Log{Desc: "leaving initACL()"}.Debug()
}()
for _, jsonPermission := range AGENTACL {
var parsedPermission mig.Permission
err = json.Unmarshal([]byte(jsonPermission), &parsedPermission)
if err != nil {
panic(err)
}
for permName, _ := range parsedPermission {
desc := fmt.Sprintf("Loading permission named '%s'", permName)
ctx.Channels.Log <- mig.Log{Desc: desc}.Debug()
}
ctx.ACL = append(ctx.ACL, parsedPermission)
}
return
}
func initMQ(orig_ctx Context) (ctx Context, err error) {
ctx = orig_ctx
defer func() {