Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
version: 2
jobs:
build-go1.20:
build-go1.23:
docker:
- image: golang:1.20
- image: golang:1.23

working_directory: /go/src/github.com/groob/moroz
steps:
Expand All @@ -15,5 +15,5 @@ workflows:
version: 2
build:
jobs:
- build-go1.20
- build-go1.23

2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ BUILD_VERSION = "\


gomodcheck:
@go help mod > /dev/null || (@echo micromdm requires Go version 1.11 or higher && exit 1)
@go help mod > /dev/null || (@echo micromdm requires Go version 1.23 or higher && exit 1)

deps: gomodcheck
@go mod download
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/groob/moroz

go 1.20
go 1.23.1

require (
github.com/BurntSushi/toml v0.2.0
Expand Down
9 changes: 6 additions & 3 deletions moroz/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func AddHTTPRoutes(r *mux.Router, e Endpoints, logger log.Logger) {
// POST /v1/santa/preflight/:id preflight request.
// POST /v1/santa/ruledownload/:id request rule updates.
// POST /v1/santa/eventupload/:id upload event.
// POST /v1/santa/postflight/:id postflight request. Implemented as a no-op.
// POST /v1/santa/postflight/:id postflight request.

r.Methods("POST").Path("/v1/santa/preflight/{id}").Handler(httptransport.NewServer(
e.PreflightEndpoint,
Expand All @@ -46,8 +46,11 @@ func AddHTTPRoutes(r *mux.Router, e Endpoints, logger log.Logger) {
options...,
))

r.Methods("POST").Path("/v1/santa/postflight/{id}").Handler(http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {},
r.Methods("POST").Path("/v1/santa/postflight/{id}").Handler(httptransport.NewServer(
e.PostflightEndpoint,
decodePostflightRequest,
encodeResponse,
options...,
))

}
Expand Down
3 changes: 3 additions & 0 deletions moroz/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,21 @@ type Service interface {
Preflight(ctx context.Context, machineID string, p santa.PreflightPayload) (*santa.Preflight, error)
RuleDownload(ctx context.Context, machineID string) ([]santa.Rule, error)
UploadEvent(ctx context.Context, machineID string, events []santa.EventPayload) error
Postflight(ctx context.Context, machineID string, p santa.PostflightPayload) (*santa.Postflight, error)
}

type Endpoints struct {
PreflightEndpoint endpoint.Endpoint
RuleDownloadEndpoint endpoint.Endpoint
EventUploadEndpoint endpoint.Endpoint
PostflightEndpoint endpoint.Endpoint
}

func MakeServerEndpoints(svc Service) Endpoints {
return Endpoints{
PreflightEndpoint: makePreflightEndpoint(svc),
RuleDownloadEndpoint: makeRuleDownloadEndpoint(svc),
EventUploadEndpoint: makeEventUploadEndpoint(svc),
PostflightEndpoint: makePostflightEndpoint(svc),
}
}
72 changes: 72 additions & 0 deletions moroz/svc_postflight.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package moroz

import (
"compress/zlib"
"context"
"encoding/json"
"net/http"
"time"

"github.com/go-kit/kit/endpoint"
"github.com/groob/moroz/santa"
)

func (svc *SantaService) Postflight(ctx context.Context, machineID string, p santa.PostflightPayload) (*santa.Postflight, error) {
return &santa.Postflight{}, nil
}

type postflightRequest struct {
MachineID string
payload santa.PostflightPayload
}

type postflightResponse struct {
*santa.Postflight
Err error `json:"error,omitempty"`
}

func (r postflightResponse) Failed() error { return r.Err }

func makePostflightEndpoint(svc Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(postflightRequest)
postflight, err := svc.Postflight(ctx, req.MachineID, req.payload)
if err != nil {
return postflightResponse{Err: err}, nil
}
return postflightResponse{Postflight: postflight}, nil
}
}

func decodePostflightRequest(ctx context.Context, r *http.Request) (interface{}, error) {
zr, err := zlib.NewReader(r.Body)
if err != nil {
return nil, err
}
defer zr.Close()
defer r.Body.Close()
id, err := machineIDFromRequest(r)
if err != nil {
return nil, err
}
req := postflightRequest{MachineID: id}
if err := json.NewDecoder(zr).Decode(&req.payload); err != nil {
return nil, err
}
return req, nil
}

func (mw logmw) Postflight(ctx context.Context, machineID string, p santa.PostflightPayload) (pf *santa.Postflight, err error) {
defer func(begin time.Time) {
_ = mw.logger.Log(
"method", "Postflight",
"machine_id", machineID,
"postflight_payload", p,
"err", err,
"took", time.Since(begin),
)
}(time.Now())

pf, err = mw.next.Postflight(ctx, machineID, p)
return
}
47 changes: 45 additions & 2 deletions santa/santa.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,21 @@ type Config struct {
}

// Rule is a Santa rule.
// Full documentation: https://github.com/google/santa/blob/01df4623c7c534568ca3d310129455ff71cc3eef/Docs/details/rules.md
// https://github.com/google/santa/blob/ff0efe952b2456b52fad2a40e6eedb0931e6bdf7/docs/development/sync-protocol.md#rules-objects
type Rule struct {
RuleType RuleType `json:"rule_type" toml:"rule_type"`
Policy Policy `json:"policy" toml:"policy"`
Identifier string `json:"identifier" toml:"identifier"`
CustomMessage string `json:"custom_msg,omitempty" toml:"custom_msg,omitempty"`
CustomUrl string `json:"custom_url,omitempty" toml:"custom_url,omitempty"`
// TODO: add support for the following fields
// CreationTime float64 `json:"creation_time,omitempty" toml:"creation_time,omitempty"`
// FileBundleBinaryCount int `json:"file_bundle_binary_count,omitempty" toml:"file_bundle_binary_count,omitempty"`
// FileBundleHash string `json:"file_bundle_hash,omitempty" toml:"file_bundle_hash,omitempty"`
}

// Preflight represents sync response sent to a Santa client by the sync server.
// https://github.com/google/santa/blob/344a35aaf63c24a56f7a021ce18ecab090584da3/docs/development/sync-protocol.md#preflight-response
type Preflight struct {
ClientMode ClientMode `json:"client_mode" toml:"client_mode"`
BlockedPathRegex string `json:"blocked_path_regex" toml:"blocked_path_regex"`
Expand All @@ -31,11 +37,18 @@ type Preflight struct {
EnableAllEventUpload bool `json:"enable_all_event_upload" toml:"enable_all_event_upload"`
EnableBundles bool `json:"enable_bundles" toml:"enable_bundles"`
EnableTransitiveRules bool `json:"enable_transitive_rules" toml:"enable_transitive_rules"`
CleanSync bool `json:"clean_sync" toml:"clean_sync"`
CleanSync bool `json:"clean_sync" toml:"clean_sync,omitempty"`
FullSyncInterval int `json:"full_sync_interval" toml:"full_sync_interval"`
// TODO: add support for sync_type and deprecate clean_sync
// SyncType string `json:"sync_type" toml:"sync_type,omitempty"`
// TODO: add in support for the following fields
// BlockUSBMount bool `json:"block_usb_mount" toml:"block_usb_mount,omitempty"`
// RemountUSBMode string `json:"remount_usb_mode" toml:"remount_usb_mode,omitempty"`
// OverrideFileAccessAction string `json:"override_file_access_action" toml:"override_file_access_action,omitempty"`
}

// A PreflightPayload represents the request sent by a santa client to the sync server.
// https://github.com/google/santa/blob/344a35aaf63c24a56f7a021ce18ecab090584da3/docs/development/sync-protocol.md#preflight-request
type PreflightPayload struct {
SerialNumber string `json:"serial_num"`
Hostname string `json:"hostname"`
Expand All @@ -49,10 +62,25 @@ type PreflightPayload struct {
CompilerRuleCount int `json:"compiler_rule_count"`
TransitiveRuleCount int `json:"transitive_rule_count"`
TeamIDRuleCount int `json:"teamid_rule_count"`
SigningIDRuleCount int `json:"signingid_rule_count"`
CdHashRuleCount int `json:"cdhash_rule_count"`
ClientMode ClientMode `json:"client_mode"`
RequestCleanSync bool `json:"request_clean_sync"`
}

// Postflight represents sync response sent to a Santa client by the sync server.
// Currently, this is a no-op.
type Postflight struct {
NoOp struct{}
}

// A PostflightPayload represents the request sent by a santa client to the sync server.
// https://github.com/google/santa/blob/344a35aaf63c24a56f7a021ce18ecab090584da3/docs/development/sync-protocol.md#postflight-request
type PostflightPayload struct {
RulesReceived int `json:"rules_received"`
RulesProcessed int `json:"rules_processed"`
}

// EventPayload represents derived metadata for events uploaded with the UploadEvent endpoint.
type EventPayload struct {
FileSHA string `json:"file_sha256"`
Expand All @@ -66,6 +94,7 @@ type EventUploadRequest struct {
}

// EventUploadEvent is a single event entry
// https://github.com/google/santa/blob/344a35aaf63c24a56f7a021ce18ecab090584da3/docs/development/sync-protocol.md#event-objects
type EventUploadEvent struct {
CurrentSessions []string `json:"current_sessions"`
Decision string `json:"decision"`
Expand Down Expand Up @@ -94,6 +123,7 @@ type EventUploadEvent struct {
SigningChain []SigningEntry `json:"signing_chain"`
SigningID string `json:"signing_id"`
TeamID string `json:"team_id"`
CdHash string `json:"cd_hash"`
}

// SigningEntry is optionally present when an event includes a binary that is signed
Expand Down Expand Up @@ -128,6 +158,15 @@ const (
// with the Apple developer certificate used to sign the application.
// ie. EQHXZ8M8AV:com.google.Chrome
SigningID

// CDHash rules use a binary's code directory hash as an identifier. This is the most specific rule in Santa.
// The code directory hash identifies a specific version of a program, similar to a file hash.
// Note that the operating system evaluates the cdhash lazily, only verifying pages of code when they're mapped in.
// This means that it is possible for a file hash to change, but a binary could still execute as long as modified
// pages are not mapped in. Santa only considers CDHash rules for processes that have CS_KILL or CS_HARD
// codesigning flags set to ensure that a process will be killed if the CDHash was tampered with
// (assuming the system has SIP enabled).
CdHash
)

func (r *RuleType) UnmarshalText(text []byte) error {
Expand All @@ -140,6 +179,8 @@ func (r *RuleType) UnmarshalText(text []byte) error {
*r = TeamID
case "SIGNINGID":
*r = SigningID
case "CDHASH":
*r = CdHash
default:
return errors.Errorf("unknown rule_type value %q", t)
}
Expand All @@ -156,6 +197,8 @@ func (r RuleType) MarshalText() ([]byte, error) {
return []byte("TEAMID"), nil
case SigningID:
return []byte("SIGNINGID"), nil
case CdHash:
return []byte("CDHASH"), nil
default:
return nil, errors.Errorf("unknown rule_type %d", r)
}
Expand Down
16 changes: 10 additions & 6 deletions santa/santa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ func TestConfigMarshalUnmarshal(t *testing.T) {
t.Errorf("have client_mode %d, want %d\n", have, want)
}

if have, want := conf.CleanSync, true; have != want {
t.Errorf("have clean_sync %t, want %t\n", have, want)
}

if have, want := conf.FullSyncInterval, 600; have != want {
t.Errorf("have full_sync_interval %d, want %d\n", have, want)
}
Expand All @@ -43,6 +39,10 @@ func TestConfigMarshalUnmarshal(t *testing.T) {
t.Errorf("have rule_type %d, want %d\n", have, want)
}

if have, want := conf.Rules[4].RuleType, CdHash; have != want {
t.Errorf("have rule_type %d, want %d\n", have, want)
}

if have, want := conf.Rules[0].Policy, Blocklist; have != want {
t.Errorf("have policy %d, want %d\n", have, want)
}
Expand All @@ -51,13 +51,17 @@ func TestConfigMarshalUnmarshal(t *testing.T) {
t.Errorf("have policy %d, want %d\n", have, want)
}

if have, want := conf.Rules[4].Policy, AllowlistCompiler; have != want {
if have, want := conf.Rules[5].Policy, AllowlistCompiler; have != want {
t.Errorf("have policy %d, want %d\n", have, want)
}

if have, want := conf.Rules[5].Policy, Remove; have != want {
if have, want := conf.Rules[6].Policy, Remove; have != want {
t.Errorf("have policy %d, want %d\n", have, want)
}

if have, want := conf.Rules[10].CustomUrl, "https://go.dev"; have != want {
t.Errorf("have custom_url %s, want %s\n", have, want)
}
}

func testConfig(t *testing.T, path string, replace bool) Config {
Expand Down
13 changes: 13 additions & 0 deletions santa/testdata/config_a_toml.golden
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ full_sync_interval = 600
identifier = "EQHXZ8M8AV:com.google.Chrome"
custom_msg = "allow google chrome signing id"

[[rules]]
rule_type = "CDHASH"
policy = "ALLOWLIST"
identifier = "935bc0fb5ca31ed7e508b9c5883630fb5b96793a"
custom_msg = "allow google chrome cdhash"

[[rules]]
rule_type = "BINARY"
policy = "ALLOWLIST_COMPILER"
Expand Down Expand Up @@ -61,3 +67,10 @@ full_sync_interval = 600
policy = "ALLOWLIST_COMPILER"
identifier = "d867fca68bbd7db18e9ced231800e7535bc067852b1e530987bb7f57b5e3a02c"
custom_msg = "allowlist go compiler component"

[[rules]]
rule_type = "BINARY"
policy = "BLOCKLIST"
identifier = "d867fca68bbd7db18e9ced231800e7535bc067852b1e530987bb7f57b5e3a02c"
custom_msg = "deny go compiler component"
custom_url = "https://go.dev"
44 changes: 44 additions & 0 deletions tools/dev/postflight/create_postflight_data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package main

import (
"bytes"
"compress/zlib"
"encoding/json"
"log"
"os"
)

var filename = "postflightdata.zlib"

// PostflightPayload represents the payload
type PostflightPayload struct {
RulesReceived int `json:"rules_received"`
RulesProcessed int `json:"rules_processed"`
}

func main() {
payload := PostflightPayload{
RulesReceived: 10,
RulesProcessed: 8,
}

jsonData, err := json.Marshal(payload)
if err != nil {
log.Fatalf("JSON marshalling failed: %s", err)
}

var zlibBuffer bytes.Buffer
zlibWriter := zlib.NewWriter(&zlibBuffer)
_, err = zlibWriter.Write(jsonData)
if err != nil {
log.Fatalf("zlib writing failed: %s", err)
}
zlibWriter.Close()

err = os.WriteFile(filename, zlibBuffer.Bytes(), 0644)
if err != nil {
log.Fatalf("Writing compressed data to file failed: %s", err)
}

log.Printf("Postflight data written to %s\n", filename)
}
Binary file added tools/dev/postflight/postflightdata.zlib
Binary file not shown.
Loading