Skip to content

Commit

Permalink
fix: Dynamically allow domains from CNAME record + wait on agent (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
fallard84 authored Jun 26, 2024
1 parent d50cb56 commit a241a67
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 42 deletions.
1 change: 0 additions & 1 deletion .github/workflows/bullfrog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ jobs:
egress-policy: block
allowed-domains: |
*.github.com
results-receiver.actions.githubusercontent.com.luykedtozraezimdvwvtsy4hga.cx.internal.cloudapp.net
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e
Expand Down
1 change: 1 addition & 0 deletions action/dist/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -19093,6 +19093,7 @@ async function startAgent({
detached: true
}
).unref();
await new Promise((resolve) => setTimeout(resolve, 5e3));
}
async function _main() {
const { allowedDomains, allowedIps, dnsPolicy, egressPolicy, logDirectory } = parseInputs();
Expand Down
4 changes: 2 additions & 2 deletions action/dist/main.js.map

Large diffs are not rendered by default.

52 changes: 34 additions & 18 deletions action/dist/post.js
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ var require_tunnel = __commonJS({
connectOptions.headers = connectOptions.headers || {};
connectOptions.headers["Proxy-Authorization"] = "Basic " + new Buffer(connectOptions.proxyAuth).toString("base64");
}
debug("making CONNECT request");
debug2("making CONNECT request");
var connectReq = self.request(connectOptions);
connectReq.useChunkedEncodingByDefault = false;
connectReq.once("response", onResponse);
Expand All @@ -733,7 +733,7 @@ var require_tunnel = __commonJS({
connectReq.removeAllListeners();
socket.removeAllListeners();
if (res.statusCode !== 200) {
debug(
debug2(
"tunneling socket could not be established, statusCode=%d",
res.statusCode
);
Expand All @@ -745,21 +745,21 @@ var require_tunnel = __commonJS({
return;
}
if (head.length > 0) {
debug("got illegal response body from proxy");
debug2("got illegal response body from proxy");
socket.destroy();
var error = new Error("got illegal response body from proxy");
error.code = "ECONNRESET";
options.request.emit("error", error);
self.removeSocket(placeholder);
return;
}
debug("tunneling connection has established");
debug2("tunneling connection has established");
self.sockets[self.sockets.indexOf(placeholder)] = socket;
return cb(socket);
}
function onError(cause) {
connectReq.removeAllListeners();
debug(
debug2(
"tunneling socket could not be established, cause=%s\n",
cause.message,
cause.stack
Expand Down Expand Up @@ -821,9 +821,9 @@ var require_tunnel = __commonJS({
}
return target;
}
var debug;
var debug2;
if (process.env.NODE_DEBUG && /\btunnel\b/.test(process.env.NODE_DEBUG)) {
debug = function() {
debug2 = function() {
var args = Array.prototype.slice.call(arguments);
if (typeof args[0] === "string") {
args[0] = "TUNNEL: " + args[0];
Expand All @@ -833,10 +833,10 @@ var require_tunnel = __commonJS({
console.error.apply(console, args);
};
} else {
debug = function() {
debug2 = function() {
};
}
exports2.debug = debug;
exports2.debug = debug2;
}
});

Expand Down Expand Up @@ -18887,10 +18887,10 @@ Support boolean input list: \`true | True | TRUE | false | False | FALSE\``);
return process.env["RUNNER_DEBUG"] === "1";
}
exports2.isDebug = isDebug;
function debug(message) {
function debug2(message) {
command_1.issueCommand("debug", {}, message);
}
exports2.debug = debug;
exports2.debug = debug2;
function error(message, properties = {}) {
command_1.issueCommand("error", utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message);
}
Expand Down Expand Up @@ -18977,6 +18977,7 @@ var import_node_child_process = require("node:child_process");
var core = __toESM(require_core());

// src/constants.ts
var AGENT_LOG_FILENAME = "agent.log";
var CONNECT_LOG_FILENAME = "connect.log";
var AUDIT = "audit";
var BLOCK = "block";
Expand Down Expand Up @@ -19017,9 +19018,9 @@ async function printAnnotations({
const correlatedData = await getCorrelateData({ connectLogFilepath });
const { egressPolicy } = parseInputs();
const result = egressPolicy === BLOCK ? "Blocked" : "Unauthorized";
console.log("\n\nCorrelated data:\n");
core2.debug("\n\nCorrelated data:\n");
correlatedData.forEach((data) => {
console.log(JSON.stringify(data));
core2.debug(JSON.stringify(data));
if (data.decision !== "blocked") {
return;
}
Expand All @@ -19041,7 +19042,7 @@ async function printAnnotations({
});
return;
} catch (error) {
console.log("No annotations found");
core2.debug("No annotations found");
}
}
async function getOutboundConnections({
Expand Down Expand Up @@ -19098,11 +19099,11 @@ async function getCorrelateData({
}) {
await new Promise((resolve) => setTimeout(resolve, 5e3));
const connections = await getOutboundConnections({ connectLogFilepath });
console.log("\n\nConnections:\n");
connections.forEach((c) => console.log(JSON.stringify(c)));
core2.debug("\n\nConnections:\n");
connections.forEach((c) => core2.debug(JSON.stringify(c)));
const decisions = await getDecisions();
console.log("\nDecisions:\n");
decisions.forEach((d) => console.log(JSON.stringify(d)));
core2.debug("\nDecisions:\n");
decisions.forEach((d) => core2.debug(JSON.stringify(d)));
const correlatedData = [];
for (const connection of connections) {
let decision = decisions.find(
Expand Down Expand Up @@ -19135,10 +19136,25 @@ async function getCorrelateData({
}
return correlatedData;
}
async function printAgentLogs({
agentLogFilepath
}) {
try {
const log = await import_promises.default.readFile(agentLogFilepath, "utf8");
const lines = log.split("\n");
for (const line of lines) {
core2.debug(line);
}
} catch (error) {
console.error("Error reading log file", error);
}
}
async function _main() {
const { logDirectory } = parseInputs();
const connectLogFilepath = import_node_path.default.join(logDirectory, CONNECT_LOG_FILENAME);
const agentLogFilepath = import_node_path.default.join(logDirectory, AGENT_LOG_FILENAME);
await printAnnotations({ connectLogFilepath });
await printAgentLogs({ agentLogFilepath });
}
async function main() {
try {
Expand Down
6 changes: 3 additions & 3 deletions action/dist/post.js.map

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions action/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ async function startAgent({
detached: true,
}
).unref();
// TODO: Use a more reliable method to wait for the agent to be ready
await new Promise((resolve) => setTimeout(resolve, 5000));
}

async function _main() {
Expand Down
34 changes: 26 additions & 8 deletions action/src/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import util from "node:util";
import { exec as execCb } from "node:child_process";
import { parseInputs } from "./inputs";
import path from "node:path";
import { BLOCK, CONNECT_LOG_FILENAME } from "./constants";
import { AGENT_LOG_FILENAME, BLOCK, CONNECT_LOG_FILENAME } from "./constants";

const exec = util.promisify(execCb);

Expand All @@ -20,10 +20,10 @@ async function printAnnotations({
const { egressPolicy } = parseInputs();
const result = egressPolicy === BLOCK ? "Blocked" : "Unauthorized";

console.log("\n\nCorrelated data:\n");
core.debug("\n\nCorrelated data:\n");

correlatedData.forEach((data) => {
console.log(JSON.stringify(data));
core.debug(JSON.stringify(data));
if (data.decision !== "blocked") {
return;
}
Expand All @@ -45,7 +45,7 @@ async function printAnnotations({
});
return;
} catch (error) {
console.log("No annotations found");
core.debug("No annotations found");
}
}

Expand Down Expand Up @@ -129,12 +129,12 @@ async function getCorrelateData({
await new Promise((resolve) => setTimeout(resolve, 5000));

const connections = await getOutboundConnections({ connectLogFilepath });
console.log("\n\nConnections:\n");
connections.forEach((c) => console.log(JSON.stringify(c)));
core.debug("\n\nConnections:\n");
connections.forEach((c) => core.debug(JSON.stringify(c)));

const decisions = await getDecisions();
console.log("\nDecisions:\n");
decisions.forEach((d) => console.log(JSON.stringify(d)));
core.debug("\nDecisions:\n");
decisions.forEach((d) => core.debug(JSON.stringify(d)));
const correlatedData: CorrelatedData[] = [];
for (const connection of connections) {
let decision = decisions.find(
Expand Down Expand Up @@ -168,11 +168,29 @@ async function getCorrelateData({
return correlatedData;
}

async function printAgentLogs({
agentLogFilepath,
}: {
agentLogFilepath: string;
}) {
try {
const log = await fs.readFile(agentLogFilepath, "utf8");
const lines = log.split("\n");
for (const line of lines) {
core.debug(line);
}
} catch (error) {
console.error("Error reading log file", error);
}
}

async function _main() {
const { logDirectory } = parseInputs();
const connectLogFilepath = path.join(logDirectory, CONNECT_LOG_FILENAME);
const agentLogFilepath = path.join(logDirectory, AGENT_LOG_FILENAME);

await printAnnotations({ connectLogFilepath });
await printAgentLogs({ agentLogFilepath });
}

async function main() {
Expand Down
Binary file modified agent/agent
Binary file not shown.
28 changes: 20 additions & 8 deletions agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,16 +252,16 @@ func main() {
if dnsLayer := packet.Layer(layers.LayerTypeDNS); dnsLayer != nil {

dns, _ := dnsLayer.(*layers.DNS)
// fmt.Println("DNS ID: ", dns.ID)
// fmt.Println("DNS QR: ", dns.QR) // true if this is a response, false if it's a query
// fmt.Println("DNS OpCode: ", dns.OpCode)
// fmt.Println("DNS ResponseCode: ", dns.ResponseCode)
// for _, q := range dns.Questions {
// fmt.Printf("DNS Question: %s %s\n", q.Name, q.Type)
// }
fmt.Println("DNS ID: ", dns.ID)
fmt.Println("DNS QR: ", dns.QR) // true if this is a response, false if it's a query
fmt.Println("DNS OpCode: ", dns.OpCode)
fmt.Println("DNS ResponseCode: ", dns.ResponseCode)
for _, q := range dns.Questions {
fmt.Printf("DNS Question: %s %s\n", q.Name, q.Type)
}
if blockDNS && !dns.QR {
for _, q := range dns.Questions {
if q.Type == layers.DNSTypeA {
if q.Type == layers.DNSTypeA || q.Type == layers.DNSTypeCNAME {
domain := string(q.Name)
fmt.Printf("DNS Question: %s %s\n", q.Name, q.Type)
fmt.Print(domain)
Expand Down Expand Up @@ -304,6 +304,18 @@ func main() {
fmt.Println("-> Unallowed request")
}
}
} else if a.Type == layers.DNSTypeCNAME { // dynamically add cname records to the allowlist
cnameDomain := string(a.CNAME)
fmt.Printf("DNS Answer: %s %s %s\n", a.Name, a.Type, cnameDomain)
fmt.Printf("%s:%s", a.Name, cnameDomain)
domainQuery := string(dns.Questions[0].Name)
if isDomainAllowed(domainQuery, allowedDomains) {
fmt.Println("-> Allowed request")
if !allowedDomains[cnameDomain] {
fmt.Printf("Adding %s to the allowed domains list\n", cnameDomain)
allowedDomains[cnameDomain] = true
}
}
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions test/make_http_requests.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#!/bin/bash

if ! timeout 3 curl https://www.google.com --output /dev/null; then
if ! timeout 5 curl https://www.google.com --output /dev/null; then
echo 'Expected curl to www.google.com to succeed, but it failed';
exit 1;
fi;

if timeout 3 curl https://www.bing.com --output /dev/null; then
if timeout 5 curl https://www.bing.com --output /dev/null; then
echo 'Expected curl to www.bing.com to fail, but it succeeded';
exit 1;
fi;
Expand Down

0 comments on commit a241a67

Please sign in to comment.