firewall: changes to initial structure

This commit is contained in:
Elle Mouton 2022-06-08 11:25:02 +02:00
parent f1d65e51e5
commit edf195ebd9
No known key found for this signature in database
GPG key ID: D7D916376026F177
4 changed files with 73 additions and 52 deletions

View file

@ -52,6 +52,9 @@ type InterceptMetaInfo struct {
// that is issuing this request.
ActorName string `json:"actor_name"`
// Feature is the feature that caused the actor to execute this action.
Feature string `json:"feature"`
// Trigger is the action or condition that triggered this intercepted
// request to be made.
Trigger string `json:"trigger"`
@ -59,6 +62,10 @@ type InterceptMetaInfo struct {
// Intent is the desired outcome or end condition this request aims to
// arrive at.
Intent string `json:"intent"`
// StructuredJsonData is extra, structured, info that the Autopilot can
// send to Lit. It is a json serialised string.
StructuredJsonData string `json:"structured_json_data"`
}
// ToCaveat returns the full custom caveat string representation of the
@ -96,23 +103,24 @@ func ParseMetaInfoCaveat(caveat string) (*InterceptMetaInfo, error) {
return i, nil
}
// InterceptRule is the JSON serializable struct containing all the rules and
// InterceptRules is the JSON serializable struct containing all the rules and
// their limits/settings that need to be enforced on a request made by an
// automated node management software against LiT. The rule information is added
// as a custom macaroon caveat.
type InterceptRule struct {
// Name is the name of the rule. It must correspond to a
Name string `json:"name"`
type InterceptRules struct {
// SessionRules are rules that apply session wide. The map is rule
// name to rule value.
SessionRules map[string]string `json:"session_rules"`
// Restrictions is a key/value map of all the parameters that apply to
// this rule.
Restrictions map[string]string `json:"restrictions"`
// Feature rules are rules that apply to a specific feature. The map is
// feature name to a map of rule name to rule value.
FeatureRules map[string]map[string]string `json:"feature_rules"`
}
// RulesToCaveat encodes a list of rules as a full custom caveat string
// representation in this format:
// lnd-custom lit-mac-fw rules:[<array_of_JSON_encoded_rules>]
func RulesToCaveat(rules []*InterceptRule) (string, error) {
func RulesToCaveat(rules *InterceptRules) (string, error) {
jsonBytes, err := json.Marshal(rules)
if err != nil {
return "", fmt.Errorf("error JSON marshaling: %v", err)
@ -122,7 +130,7 @@ func RulesToCaveat(rules []*InterceptRule) (string, error) {
}
// ParseRuleCaveat tries to parse the given caveat string as a rule struct.
func ParseRuleCaveat(caveat string) ([]*InterceptRule, error) {
func ParseRuleCaveat(caveat string) (*InterceptRules, error) {
if !strings.HasPrefix(caveat, MetaRulesFullCaveatPrefix) {
return nil, ErrNoRulesCaveat
}
@ -134,11 +142,11 @@ func ParseRuleCaveat(caveat string) ([]*InterceptRule, error) {
// There's a colon after the prefix that we need to skip as well.
jsonData := caveat[len(MetaRulesFullCaveatPrefix)+1:]
var rules []*InterceptRule
var rules InterceptRules
if err := json.Unmarshal([]byte(jsonData), &rules); err != nil {
return nil, fmt.Errorf("error unmarshaling JSON: %v", err)
}
return rules, nil
return &rules, nil
}

View file

@ -9,25 +9,27 @@ import (
const (
testMetaCaveat = "lnd-custom lit-mac-fw meta:{\"actor_name\":" +
"\"re-balancer\",\"trigger\":\"channel 7413345453234435345 " +
"depleted\",\"intent\":\"increase outbound liquidity by " +
"2000000 sats\"}"
"\"autopilot\",\"feature\":\"re-balance\",\"trigger\":" +
"\"channel 7413345453234435345 depleted\",\"intent\":" +
"\"increase outbound liquidity by 2000000 sats\"," +
"\"structured_json_data\":\"{}\"}"
testRulesCaveat = "lnd-custom lit-mac-fw rules:[{\"name\":" +
"\"re-balance-limits\",\"restrictions\":" +
"{\"first-hop-ignore-list\":\"03abcd...,02badb01...\"," +
"\"max-hops\":\"4\",\"off-chain-fees-sats\":\"10\"}}," +
"{\"name\":\"time-limits\",\"restrictions\":" +
"{\"re-balance-min-interval-seconds\":\"3600\"}}]"
testRulesCaveat = "lnd-custom lit-mac-fw rules:{\"session_rules\":{" +
"\"rate-limit\":\"1/10\"},\"feature_rules\":{\"AutoFees\":{" +
"\"first-hop-ignore-list\":\"03abcd...,02badb01...\"," +
"\"max-hops\":\"4\"},\"Rebalance\":{\"off-chain-fees-sats\":" +
"\"10\",\"re-balance-min-interval-seconds\":\"3600\"}}}"
)
// TestInterceptMetaInfo makes sure that a meta information struct can be
// formatted as a caveat and then parsed again successfully.
func TestInterceptMetaInfo(t *testing.T) {
info := &InterceptMetaInfo{
ActorName: "re-balancer",
Trigger: "channel 7413345453234435345 depleted",
Intent: "increase outbound liquidity by 2000000 sats",
ActorName: "autopilot",
Feature: "re-balance",
Trigger: "channel 7413345453234435345 depleted",
Intent: "increase outbound liquidity by 2000000 sats",
StructuredJsonData: "{}",
}
caveat, err := info.ToCaveat()
@ -90,19 +92,21 @@ func TestParseMetaInfoCaveat(t *testing.T) {
// TestInterceptRule makes sure that a rules list struct can be formatted as a
// caveat and then parsed again successfully.
func TestInterceptRule(t *testing.T) {
rules := []*InterceptRule{{
Name: "re-balance-limits",
Restrictions: map[string]string{
"off-chain-fees-sats": "10",
"max-hops": "4",
"first-hop-ignore-list": "03abcd...,02badb01...",
rules := &InterceptRules{
FeatureRules: map[string]map[string]string{
"AutoFees": {
"first-hop-ignore-list": "03abcd...,02badb01...",
"max-hops": "4",
},
"Rebalance": {
"off-chain-fees-sats": "10",
"re-balance-min-interval-seconds": "3600",
},
},
}, {
Name: "time-limits",
Restrictions: map[string]string{
"re-balance-min-interval-seconds": "3600",
SessionRules: map[string]string{
"rate-limit": "1/10",
},
}}
}
caveat, err := RulesToCaveat(rules)
require.NoError(t, err)
@ -122,7 +126,7 @@ func TestParseRulesCaveat(t *testing.T) {
name string
input string
err error
result []*InterceptRule
result *InterceptRules
}{{
name: "empty string",
input: "",
@ -138,23 +142,25 @@ func TestParseRulesCaveat(t *testing.T) {
"'b' looking for beginning of value"),
}, {
name: "empty JSON",
input: "lnd-custom lit-mac-fw rules:[]",
result: []*InterceptRule{},
}, {
name: "empty rules",
input: "lnd-custom lit-mac-fw rules:[{}, {}]",
result: []*InterceptRule{{}, {}},
input: "lnd-custom lit-mac-fw rules:{}",
result: &InterceptRules{},
}, {
name: "valid rules",
input: "lnd-custom lit-mac-fw rules:[{\"name\":\"foo\"}, " +
"{\"restrictions\":{\"foo\":\"bar\"}}]",
result: []*InterceptRule{{
Name: "foo",
}, {
Restrictions: map[string]string{
"foo": "bar",
input: "lnd-custom lit-mac-fw rules:{\"session_rules\":" +
"{\"rate-limit\":\"2000\"}, \"feature_rules\":" +
"{\"Autofees\":{\"foo\":\"bar\", \"rate-limit\":" +
"\"1000\"}}}",
result: &InterceptRules{
FeatureRules: map[string]map[string]string{
"Autofees": {
"foo": "bar",
"rate-limit": "1000",
},
},
}},
SessionRules: map[string]string{
"rate-limit": "2000",
},
},
}}
for _, tc := range testCases {

View file

@ -30,11 +30,13 @@ type RequestInfo struct {
MWRequestType string
URI string
GRPCMessageType string
IsError bool
Serialized []byte
Streaming bool
Macaroon *macaroon.Macaroon
Caveats []string
MetaInfo *InterceptMetaInfo
Rules []*InterceptRule
Rules *InterceptRules
}
// NewInfoFromRequest parses the given RPC middleware interception request and
@ -54,6 +56,8 @@ func NewInfoFromRequest(req *lnrpc.RPCMiddlewareRequest) (*RequestInfo, error) {
MWRequestType: MWRequestTypeRequest,
URI: t.Request.MethodFullUri,
GRPCMessageType: t.Request.TypeName,
IsError: t.Request.IsError,
Serialized: t.Request.Serialized,
Streaming: t.Request.StreamRpc,
}
@ -62,6 +66,8 @@ func NewInfoFromRequest(req *lnrpc.RPCMiddlewareRequest) (*RequestInfo, error) {
MWRequestType: MWRequestTypeResponse,
URI: t.Response.MethodFullUri,
GRPCMessageType: t.Response.TypeName,
IsError: t.Response.IsError,
Serialized: t.Response.Serialized,
Streaming: t.Response.StreamRpc,
}

View file

@ -53,9 +53,10 @@ func (r *RuleEnforcer) Intercept(_ context.Context,
log.Infof("Enforcing rule %v", ri)
// Enforce actual rules.
if len(ri.Rules) > 0 {
numRules := len(ri.Rules.SessionRules) + len(ri.Rules.FeatureRules)
if numRules > 0 {
// TODO(guggero): Implement rules and their enforcement.
log.Debugf("There are %d rules to enforce", len(ri.Rules))
log.Debugf("There are %d rules to enforce", numRules)
}
// Send empty response, accepting the request.