зеркало из https://github.com/mozilla/mig.git
Коммит
586342378e
4
Makefile
4
Makefile
|
@ -54,6 +54,8 @@ mig-action-verifier: gpgme
|
|||
$(MKDIR) -p $(BINDIR)
|
||||
$(GO) build $(GOOPTS) -o $(BINDIR)/mig-action-verifier $(GOLDFLAGS) mig/clients/verifier
|
||||
|
||||
go_get_deps_into_system:
|
||||
make GOGETTER="go get -u" go_get_deps
|
||||
|
||||
go_get_deps:
|
||||
$(GOGETTER) code.google.com/p/go.crypto/openpgp
|
||||
|
@ -123,4 +125,4 @@ clean:
|
|||
clean-all: clean
|
||||
rm -rf pkg
|
||||
|
||||
.PHONY: clean clean-all gpgme
|
||||
.PHONY: clean clean-all gpgme go_get_deps_into_system
|
||||
|
|
|
@ -58,6 +58,37 @@ var HEARTBEATFREQ time.Duration = 300 * time.Second
|
|||
// timeout after which a module run is killed
|
||||
var MODULETIMEOUT time.Duration = 300 * time.Second
|
||||
|
||||
// Control modules permissions by PGP keys
|
||||
var AGENTACL = [...]string{
|
||||
`{
|
||||
"default": {
|
||||
"minimumweight": 2,
|
||||
"investigators": {
|
||||
"Bob Kelso": {
|
||||
"fingerprint": "E60892BB....",
|
||||
"weight": 2
|
||||
},
|
||||
"Morpheus": {
|
||||
"fingerprint": "AD595634....",
|
||||
"weight": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
}`,
|
||||
`{
|
||||
"pidkill": {
|
||||
"minimumweight": 1,
|
||||
"investigators": {
|
||||
"MIG Scheduler": {
|
||||
"fingerprint": "E60892BB...",
|
||||
"weight": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}`,
|
||||
}
|
||||
|
||||
|
||||
// PGP public keys that are authorized to sign actions
|
||||
// this is an array of strings, put each public key block
|
||||
// into its own array entry, as shown below
|
||||
|
|
125
doc/concepts.rst
125
doc/concepts.rst
|
@ -112,20 +112,19 @@ The parameters are:
|
|||
|
||||
Upon generation, additional fields are appended to the action:
|
||||
|
||||
* PGPSignature: all of the parameters above are concatenated into a string and
|
||||
* PGPSignatures: all of the parameters above are concatenated into a string and
|
||||
signed with the investigator's private GPG key. The signature is part of the
|
||||
action, and used by agents to verify that an action comes from a trusted
|
||||
investigator.
|
||||
* PGPSignatureDate: is the date of the PGP signature, used as a timestamp of
|
||||
the action creation.
|
||||
investigator. `PGPSignatures` is an array that contains one or more signature
|
||||
from authorized investigators.
|
||||
* ValidFrom and ExpireAt: two dates that constrains the validity of the action
|
||||
to a time window.
|
||||
to a UTC time window.
|
||||
|
||||
Actions files are submitted to the API or the Scheduler directly. Eventually,
|
||||
the PGP signature will be verified by intermediary components, and in any case
|
||||
by each agent before execution.
|
||||
Actions files are submitted to the API or the Scheduler directly. The PGP
|
||||
Signatures are always verified by the agents, and can optionally be verified by
|
||||
other components along the way.
|
||||
Additional attributes are added to the action by the scheduler. Those are
|
||||
defined as "MetaAction" and are used to track the action status.
|
||||
defined as `ExtendedAction` and are used to track the action status.
|
||||
|
||||
Commands
|
||||
~~~~~~~~
|
||||
|
@ -186,6 +185,114 @@ While the result is negative, the command itself has succeeded. Had a failure
|
|||
happened on the agent, the scheduler would have been notified and the status
|
||||
would be one of "failed", "timeout" or "cancelled".
|
||||
|
||||
Access Control Lists
|
||||
--------------------
|
||||
|
||||
Not all keys can perform all actions. The scheduler, for example, sometimes need
|
||||
to issue specific actions to agents (such as during the upgrade protocol) but
|
||||
shouldn't be able to perform more dangerous actions. This is enforced by
|
||||
an Access Control List, or ACL, stored on the agents. An ACL describes who can
|
||||
access what function of which module. It can be used to require multiple
|
||||
signatures on specific actions, and limit the list of investigators allowed to
|
||||
perform an action.
|
||||
|
||||
An ACL is composed of permissions, which are JSON documents hardwired into
|
||||
the agent configuration. In the future, MIG will dynamically ship permissions
|
||||
to agents.
|
||||
|
||||
Below is an example of a permission for the `filechecker` module:
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"filechecker": {
|
||||
"minimumweight": 2,
|
||||
"investigators": {
|
||||
"Bob Kelso": {
|
||||
"fingerprint": "E60892BB9BD...",
|
||||
"weight": 2
|
||||
},
|
||||
"John Smith": {
|
||||
"fingerprint": "9F759A1A0A3...",
|
||||
"weight": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
`investigators` contains a list of users with their PGP fingerprints, and their
|
||||
weight, an integer that represents their access level.
|
||||
When an agent receives an action that calls the filechecker module, it will
|
||||
first verify the signatures of the action, and then validates that the signers
|
||||
are authorized to perform the action. This is done by summing up the weights of
|
||||
the signatures, and verifying that they equal or exceed the minimum required
|
||||
weight.
|
||||
|
||||
Thus, in the example above, investigator John Smith cannot issue a filechecker
|
||||
action alone. His weight of 1 doesn't satisfy the minimum weight of 2 required
|
||||
by the filechecker permission. Therefore, John will need to ask investigator Bob
|
||||
Kelso to sign his action as well. The weight of both investigators are then
|
||||
added, giving a total of 3, which satisfies the minimum weight of 2.
|
||||
|
||||
This method gives ample flexibility to require multiple signatures on modules,
|
||||
and ensure that one investigator cannot perform sensitive actions on remote
|
||||
endpoints without the permissions of others.
|
||||
|
||||
The default permission `default` can be used as a default for all modules. It
|
||||
has the following syntax:
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"default": {
|
||||
"minimumweight": 2,
|
||||
"investigators": { ... }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
The `default` permission is overridden by module specific permissions.
|
||||
|
||||
The ACL is currently applied to modules. In the future, ACL will have finer
|
||||
control to authorize access to specific functions of modules. For example, an
|
||||
investigator could be authorized to call the `regex` function of filechecker
|
||||
module, but only in `/etc`. This functionality is not implemented yet.
|
||||
|
||||
Extracting PGP fingerprints from public keys
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
On Linux, the `gpg` command can easily display the fingerprint of a key using
|
||||
`gpg --fingerprint <key id>`. For example:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
$ gpg --fingerprint jvehent@mozilla.com
|
||||
pub 2048R/3B763E8F 2013-04-30
|
||||
Key fingerprint = E608 92BB 9BD8 9A69 F759 A1A0 A3D6 5217 3B76 3E8F
|
||||
uid Julien Vehent (personal) <julien@linuxwall.info>
|
||||
uid Julien Vehent (ulfr) <jvehent@mozilla.com>
|
||||
sub 2048R/8026F39F 2013-04-30
|
||||
|
||||
|
||||
You should always verify the trustworthiness of a key before using it:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
$ gpg --list-sigs jvehent@mozilla.com
|
||||
pub 2048R/3B763E8F 2013-04-30
|
||||
uid Julien Vehent (personal) <julien@linuxwall.info>
|
||||
sig 3 3B763E8F 2013-06-23 Julien Vehent (personal) <julien@linuxwall.info>
|
||||
sig 3 28A860CE 2013-10-04 Curtis Koenig <ckoenig@mozilla.com>
|
||||
.....
|
||||
|
||||
We want to extract the fingerprint, and obtain a 40 characters hexadecimal
|
||||
string that can used in permissions.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
$gpg --fingerprint --with-colons jvehent@mozilla.com |grep '^fpr'|cut -f 10 -d ':'
|
||||
E60892BB9BD89A69F759A1A0A3D652173B763E8F
|
||||
|
||||
Agent registration process
|
||||
--------------------------
|
||||
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/* Mozilla InvestiGator
|
||||
|
||||
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]
|
||||
|
||||
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 mig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ACL []Permission
|
||||
|
||||
type Permission map[string]struct {
|
||||
MinimumWeight int
|
||||
Investigators map[string]struct{
|
||||
Fingerprint string
|
||||
Weight int
|
||||
}
|
||||
}
|
||||
|
||||
// verifyPermission controls that the PGP keys, identified by their fingerprints, that
|
||||
// signed an operation are sufficient to allow this operation to run
|
||||
func verifyPermission(operation operation, permName string, perm Permission, fingerprints []string) (err error) {
|
||||
if perm[permName].MinimumWeight < 1 {
|
||||
return fmt.Errorf("Invalid permission '%s'. Must require at least 1 signature, has %d",
|
||||
permName, perm[permName].MinimumWeight)
|
||||
}
|
||||
signaturesWeight := 0
|
||||
for _, fp := range fingerprints {
|
||||
for _, signer := range perm[permName].Investigators {
|
||||
if strings.ToUpper(fp) == strings.ToUpper(signer.Fingerprint) {
|
||||
signaturesWeight += signer.Weight
|
||||
}
|
||||
}
|
||||
}
|
||||
if signaturesWeight < perm[permName].MinimumWeight {
|
||||
return fmt.Errorf("Permission denied for operation '%s'. Insufficient signatures weight. Need %d, got %d",
|
||||
operation.Module, perm[permName].MinimumWeight, signaturesWeight)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -43,6 +43,7 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"mig/pgp"
|
||||
"mig/pgp/verify"
|
||||
"strconv"
|
||||
"time"
|
||||
|
@ -74,22 +75,16 @@ type counters struct {
|
|||
// an Action is the json object that is created by an investigator
|
||||
// and provided to the MIG platform. It must be PGP signed.
|
||||
type Action struct {
|
||||
// meta
|
||||
ID uint64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Target string `json:"target"`
|
||||
Description description `json:"description"`
|
||||
Threat threat `json:"threat"`
|
||||
// time window
|
||||
ValidFrom time.Time `json:"validfrom"`
|
||||
ExpireAfter time.Time `json:"expireafter"`
|
||||
// operation to perform
|
||||
Operations []operation `json:"operations"`
|
||||
// action signature
|
||||
PGPSignature string `json:"pgpsignature"`
|
||||
PGPSignatureDate time.Time `json:"pgpsignaturedate"`
|
||||
// action syntax version
|
||||
SyntaxVersion int `json:"syntaxversion"`
|
||||
ID uint64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Target string `json:"target"`
|
||||
Description description `json:"description"`
|
||||
Threat threat `json:"threat"`
|
||||
ValidFrom time.Time `json:"validfrom"`
|
||||
ExpireAfter time.Time `json:"expireafter"`
|
||||
Operations []operation `json:"operations"`
|
||||
PGPSignatures []string `json:"pgpsignatures"`
|
||||
SyntaxVersion int `json:"syntaxversion"`
|
||||
}
|
||||
|
||||
// a description is a simple object that contains detail about the
|
||||
|
@ -185,29 +180,29 @@ func (a Action) Validate() (err error) {
|
|||
if a.Operations == nil {
|
||||
return errors.New("Action.Operations is nil. Expecting string.")
|
||||
}
|
||||
if a.PGPSignature == "" {
|
||||
return errors.New("Action.PGPSignature is empty. Expecting string.")
|
||||
if len(a.PGPSignatures) < 1 {
|
||||
return errors.New("Action.PGPSignatures is empty. Expecting array of strings.")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Validate verifies that the Action received contained all the
|
||||
// necessary fields, and returns an error when it doesn't.
|
||||
func (a Action) VerifySignature(keyring io.Reader) (err error) {
|
||||
// Verify the signature
|
||||
// VerifySignatures verifies that the Action contains valid signatures from
|
||||
// known investigators. It does not verify permissions.
|
||||
func (a Action) VerifySignatures(keyring io.Reader) (err error) {
|
||||
astr, err := a.String()
|
||||
if err != nil {
|
||||
return errors.New("Failed to stringify action")
|
||||
}
|
||||
valid, _, err := verify.Verify(astr, a.PGPSignature, keyring)
|
||||
if err != nil {
|
||||
return errors.New("Failed to verify PGP Signature")
|
||||
for _, sig := range a.PGPSignatures {
|
||||
valid, _, err := verify.Verify(astr, sig, keyring)
|
||||
if err != nil {
|
||||
return errors.New("Failed to verify PGP Signature")
|
||||
}
|
||||
if !valid {
|
||||
return errors.New("Invalid PGP Signature")
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
return errors.New("Invalid PGP Signature")
|
||||
}
|
||||
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
// concatenates Action components into a string
|
||||
|
@ -222,3 +217,51 @@ func (a Action) String() (str string, err error) {
|
|||
|
||||
return
|
||||
}
|
||||
|
||||
// VerifyACL controls that an action has been issued by investigators
|
||||
// that have the right permissions. This function looks at each operation
|
||||
// listed in the action, and find the corresponding permission. If no
|
||||
// permission is found, the default one `default` is used.
|
||||
// The first permission that is found to apply to an operation, but
|
||||
// doesn't allow the operation to run, will fail the verification globally
|
||||
func (a Action) VerifyACL(acl ACL, keyring io.Reader) (err error) {
|
||||
// first, verify all signatures and get a list of PGP
|
||||
// fingerprints of the signers
|
||||
var fingerprints []string
|
||||
astr, err := a.String()
|
||||
if err != nil {
|
||||
return errors.New("Failed to stringify action")
|
||||
}
|
||||
for _, sig := range a.PGPSignatures {
|
||||
fp, err := pgp.GetFingerprintFromSignature(astr, sig, keyring)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to retrieve fingerprint from signatures: %v", err)
|
||||
}
|
||||
fingerprints = append(fingerprints, fp)
|
||||
}
|
||||
|
||||
// Then, for each operation contained in the action, look for
|
||||
// a permission that apply to it, by comparing the operation name
|
||||
// with permission name. If none is found, use the default permission.
|
||||
for _, operation := range a.Operations {
|
||||
for _, permission := range acl {
|
||||
for permName, _ := range permission {
|
||||
if permName == operation.Module {
|
||||
return verifyPermission(operation, permName, permission, fingerprints)
|
||||
}
|
||||
}
|
||||
}
|
||||
// no specific permission found, apply the default permission
|
||||
var defaultPerm Permission
|
||||
for _, permission := range acl {
|
||||
for permName, _ := range permission {
|
||||
if permName == "default" {
|
||||
defaultPerm = permission
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return verifyPermission(operation, "default", defaultPerm, fingerprints)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -336,7 +336,7 @@ func createAction(respWriter http.ResponseWriter, request *http.Request) {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = action.VerifySignature(keyring)
|
||||
err = action.VerifySignatures(keyring)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
@ -115,16 +115,15 @@ func main() {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
a.PGPSignature, err = sign.Sign(str, *key)
|
||||
pgpsig, err := sign.Sign(str, *key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
a.PGPSignatureDate = time.Now().UTC()
|
||||
|
||||
a.PGPSignatures = append(a.PGPSignatures, pgpsig)
|
||||
var jsonAction []byte
|
||||
if *pretty {
|
||||
jsonAction, err = json.MarshalIndent(a, "", "\t")
|
||||
fmt.Printf("%s\n", jsonAction)
|
||||
} else {
|
||||
jsonAction, err = json.Marshal(a)
|
||||
}
|
||||
|
@ -179,7 +178,7 @@ func main() {
|
|||
}
|
||||
|
||||
// syntax checking
|
||||
err = a.VerifySignature(keyring)
|
||||
err = a.VerifySignatures(keyring)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ func main() {
|
|||
defer keyring.Close()
|
||||
|
||||
// syntax checking
|
||||
err = a.VerifySignature(keyring)
|
||||
err = a.VerifySignatures(keyring)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
@ -38,8 +38,10 @@ package pgp
|
|||
import (
|
||||
"bytes"
|
||||
"code.google.com/p/go.crypto/openpgp"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"mig/pgp/verify"
|
||||
)
|
||||
|
||||
// TransformArmoredPubKeysToKeyring takes a list of public PGP key in armored form and transforms
|
||||
|
@ -72,3 +74,19 @@ func ArmoredPubKeysToKeyring(pubkeys []string) (keyring io.Reader, keycount int,
|
|||
keyring = bytes.NewReader(buf.Bytes())
|
||||
return
|
||||
}
|
||||
|
||||
// TransformArmoredPubKeysToKeyring takes a list of public PGP key in armored form and transforms
|
||||
// it into a keyring that can be used in other openpgp's functions
|
||||
func GetFingerprintFromSignature(data string, signature string, keyring io.Reader) (fingerprint string, err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = fmt.Errorf("GetFingerprintFromSignature() -> %v", e)
|
||||
}
|
||||
}()
|
||||
_, entity, err := verify.Verify(data, signature, keyring)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fingerprint = hex.EncodeToString(entity.PrimaryKey.Fingerprint[:])
|
||||
return
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ func Verify(data string, signature string, keyring io.Reader) (valid bool, entit
|
|||
valid = false
|
||||
|
||||
// re-armor signature and transform into io.Reader
|
||||
sigReader := strings.NewReader(reArmor(signature))
|
||||
sigReader := strings.NewReader(reArmorSignature(signature))
|
||||
|
||||
// decode armor
|
||||
sigBlock, err := armor.Decode(sigReader)
|
||||
|
@ -82,9 +82,9 @@ func Verify(data string, signature string, keyring io.Reader) (valid bool, entit
|
|||
return
|
||||
}
|
||||
|
||||
// reArmor takes a single line armor and turns it back into an PGP-style
|
||||
// reArmorSignature takes a single line armor and turns it back into an PGP-style
|
||||
// multi-line armored string (thank you, camlistore folks)
|
||||
func reArmor(line string) string {
|
||||
func reArmorSignature(line string) string {
|
||||
lastEq := strings.LastIndex(line, "=")
|
||||
if lastEq == -1 {
|
||||
return ""
|
||||
|
|
Загрузка…
Ссылка в новой задаче