Skip to content

Instantly share code, notes, and snippets.

@xuxucode
Last active February 21, 2024 14:06
Show Gist options
  • Select an option

  • Save xuxucode/e365d85cf2cc4e8a844b25b55e55a89f to your computer and use it in GitHub Desktop.

Select an option

Save xuxucode/e365d85cf2cc4e8a844b25b55e55a89f to your computer and use it in GitHub Desktop.

Revisions

  1. xuxucode revised this gist Feb 16, 2024. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions apparmor-docker-default.js
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    // Usage: node apparmor-docker-default.js

    const path = require("node:path");
    const fs = require("node:fs");

  2. xuxucode revised this gist Feb 16, 2024. 1 changed file with 1 addition and 3 deletions.
    4 changes: 1 addition & 3 deletions apparmor-docker-default.js
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,3 @@
    // Usage: node apparmor-docker-default.js

    const path = require("node:path");
    const fs = require("node:fs");

    @@ -89,7 +87,7 @@ function parseTemplate(template, p) {
    // Replace "{{range $value := .InnerImports}}".
    const innerImportsStartIndex = template.indexOf("{{range $value := .InnerImports}}");
    const innerImportsEndIndex = template.indexOf(endTag, innerImportsStartIndex) + endTag.length;
    template = template.substring(0, innerImportsStartIndex) + p.innerImports.join("\n") + template.substring(innerImportsEndIndex);
    template = template.substring(0, innerImportsStartIndex) + p.innerImports.map(i => " " + i).join("\n") + template.substring(innerImportsEndIndex);

    // Replace "{{.Name}}".
    template = template.replaceAll(/\{\{\.Name\}\}/g, p.name);
  3. xuxucode created this gist Feb 16, 2024.
    106 changes: 106 additions & 0 deletions apparmor-docker-default.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,106 @@
    // Usage: node apparmor-docker-default.js

    const path = require("node:path");
    const fs = require("node:fs");

    const profileDirectory = "/etc/apparmor.d"

    /**
    * @typedef ProfileData
    * @property {string} name
    * @property {string} daemonProfile
    * @property {string[]} imports
    * @property {string[]} innerImports
    */

    // Output "docker-default" profile.
    {
    getDefault().then(profile => {
    console.log(profile);
    });
    }

    async function loadTemplate() {
    const response = await fetch("https://raw.githubusercontent.com/moby/moby/master/profiles/apparmor/template.go");
    const text = await response.text();

    const startTag = "const baseTemplate = `";
    const startIndex = text.indexOf(startTag) + startTag.length;
    const endIndex = text.lastIndexOf("`");
    return text.slice(startIndex, endIndex);
    }

    // https://github.com/moby/moby/blob/master/profiles/apparmor/apparmor.go#L59
    async function getDefault(name = "docker-default") {
    // Figure out the daemon profile.
    let currentProfile = "";
    try {
    currentProfile = fs.readFileSync("/proc/self/attr/current", "utf8").trim();
    } catch {
    // If we couldn't get the daemon profile, assume we are running
    // unconfined which is generally the default.
    }
    const daemonProfile = currentProfile.split(" ")[0] || "unconfined";

    return generateDefault(name, daemonProfile)
    }

    // https://github.com/moby/moby/blob/master/profiles/apparmor/apparmor.go#L32
    /**
    * @param {string} name
    * @param {string} daemonProfile
    */
    async function generateDefault(name, daemonProfile) {
    const template = await loadTemplate();

    /** @type {ProfileData} */
    const p = {
    name,
    daemonProfile,
    imports: [],
    innerImports: [],
    };

    if (macroExists("tunables/global")) {
    p.imports.push("#include <tunables/global>");
    } else {
    p.imports.push("@{PROC}=/proc/");
    }

    if (macroExists("abstractions/base")) {
    p.innerImports.push("#include <abstractions/base>");
    }

    return parseTemplate(template, p);
    }

    /**
    * @param {string} template
    * @param {ProfileData} p
    */
    function parseTemplate(template, p) {
    const endTag = "{{end}}";

    // Replace "{{range $value := .Imports}}".
    const importsStartIndex = template.indexOf("{{range $value := .Imports}}");
    const importsEndIndex = template.indexOf(endTag, importsStartIndex) + endTag.length;
    template = template.substring(0, importsStartIndex) + p.imports.join("\n") + template.substring(importsEndIndex);

    // Replace "{{range $value := .InnerImports}}".
    const innerImportsStartIndex = template.indexOf("{{range $value := .InnerImports}}");
    const innerImportsEndIndex = template.indexOf(endTag, innerImportsStartIndex) + endTag.length;
    template = template.substring(0, innerImportsStartIndex) + p.innerImports.join("\n") + template.substring(innerImportsEndIndex);

    // Replace "{{.Name}}".
    template = template.replaceAll(/\{\{\.Name\}\}/g, p.name);

    // Replace "{{.DaemonProfile}}"
    template = template.replaceAll(/\{\{\.DaemonProfile\}\}/g, p.daemonProfile);

    return template;
    }

    // https://github.com/moby/moby/blob/master/profiles/apparmor/apparmor.go#L52
    function macroExists(m) {
    return fs.existsSync(path.join(profileDirectory, m));
    }