diff options
Diffstat (limited to 'libexec/nuageinit/nuageinit')
-rwxr-xr-x | libexec/nuageinit/nuageinit | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/libexec/nuageinit/nuageinit b/libexec/nuageinit/nuageinit new file mode 100755 index 000000000000..08224061d1b1 --- /dev/null +++ b/libexec/nuageinit/nuageinit @@ -0,0 +1,312 @@ +#!/usr/libexec/flua + +-- SPDX-License-Identifier: BSD-2-Clause-FreeBSD +-- +-- Copyright(c) 2022 Baptiste Daroussin <bapt@FreeBSD.org> + +local nuage = require("nuage") +local yaml = require("yaml") + +if #arg ~= 2 then + nuage.err("Usage ".. arg[0] .." <cloud-init directory> [config-2|nocloud]") +end +local path = arg[1] +local citype = arg[2] +local ucl = require("ucl") + +local default_user = { + name = "freebsd", + homedir = "/home/freebsd", + groups = "wheel", + gecos = "FreeBSD User", + shell = "/bin/sh", + plain_text_passwd = "freebsd" +} + +local root = os.getenv("NUAGE_FAKE_ROOTDIR") +if not root then + root = "" +end + +local function open_config(name) + nuage.mkdir_p(root .. "/etc/rc.conf.d") + local f,err = io.open(root .. "/etc/rc.conf.d/" .. name, "w") + if not f then + nuage.err("nuageinit: unable to open "..name.." config: " .. err) + end + return f +end + +local function get_ifaces() + local parser = ucl.parser() + -- grab ifaces + local ns = io.popen('netstat -i --libxo json') + local netres = ns:read("*a") + ns:close() + local res,err = parser:parse_string(netres) + if not res then + nuage.warn("Error parsing netstat -i --libxo json outout: " .. err) + return nil + end + local ifaces = parser:get_object() + local myifaces = {} + for _,iface in pairs(ifaces["statistics"]["interface"]) do + if iface["network"]:match("<Link#%d>") then + local s = iface["address"] + myifaces[s:lower()] = iface["name"] + end + end + return myifaces +end + +local function config2_network(p) + local parser = ucl.parser() + local f = io.open(p .. "/network_data.json") + if not f then + -- silently return no network configuration is provided + return + end + f:close() + local res,err = parser:parse_file(p .. "/network_data.json") + if not res then + nuage.warn("nuageinit: error parsing network_data.json: " .. err) + return + end + local obj = parser:get_object() + + local ifaces = get_ifaces() + if not ifaces then + nuage.warn("nuageinit: no network interfaces found") + return + end + local mylinks = {} + for _,v in pairs(obj["links"]) do + local s = v["ethernet_mac_address"]:lower() + mylinks[v["id"]] = ifaces[s] + end + + nuage.mkdir_p(root .. "/etc/rc.conf.d") + local network = open_config("network") + local routing = open_config("routing") + local ipv6 = {} + local ipv6_routes = {} + local ipv4 = {} + for _,v in pairs(obj["networks"]) do + local interface = mylinks[v["link"]] + if v["type"] == "ipv4_dhcp" then + network:write("ifconfig_"..interface.."=\"DHCP\"\n") + end + if v["type"] == "ipv4" then + network:write("ifconfig_"..interface.."=\"inet "..v["ip_address"].." netmask " .. v["netmask"] .. "\"\n") + if v["gateway"] then + routing:write("defaultrouter=\""..v["gateway"].."\"\n") + end + if v["routes"] then + for i,r in ipairs(v["routes"]) do + local rname = "cloudinit" .. i .. "_" .. interface + if v["gateway"] and v["gateway"] == r["gateway"] then goto next end + if r["network"] == "0.0.0.0" then + routing:write("defaultrouter=\""..r["gateway"].."\"\n") + goto next + end + routing:write("route_".. rname .. "=\"-net ".. r["network"] .. " ") + routing:write(r["gateway"] .. " " .. r["netmask"] .. "\"\n") + ipv4[#ipv4 + 1] = rname + ::next:: + end + end + end + if v["type"] == "ipv6" then + ipv6[#ipv6+1] = interface + ipv6_routes[#ipv6_routes+1] = interface + network:write("ifconfig_"..interface.."_ipv6=\"inet6 "..v["ip_address"].."\"\n") + if v["gateway"] then + routing:write("ipv6_defaultrouter=\""..v["gateway"].."\"\n") + routing:write("ipv6_route_"..interface.."=\""..v["gateway"]) + routing:write(" -prefixlen 128 -interface "..interface.."\"\n") + end + -- TODO compute the prefixlen for the routes + --if v["routes"] then + -- for i,r in ipairs(v["routes"]) do + -- local rname = "cloudinit" .. i .. "_" .. mylinks[v["link"]] + -- -- skip all the routes which are already covered by the default gateway, some provider + -- -- still list plenty of them. + -- if v["gateway"] == r["gateway"] then goto next end + -- routing:write("ipv6_route_" .. rname .. "\"\n") + -- ipv6_routes[#ipv6_routes+1] = rname + -- ::next:: + -- end + --end + end + end + if #ipv4 > 0 then + routing:write("static_routes=\"") + routing:write(table.concat(ipv4, " ") .. "\"\n") + end + if #ipv6 > 0 then + network:write("ipv6_network_interfaces=\"") + network:write(table.concat(ipv6, " ") .. "\"\n") + network:write("ipv6_default_interface=\""..ipv6[1].."\"\n") + end + if #ipv6_routes > 0 then + routing:write("ipv6_static_routes=\"") + routing:write(table.concat(ipv6, " ") .. "\"\n") + end + network:close() + routing:close() +end + +if citype == "config-2" then + local parser = ucl.parser() + local res,err = parser:parse_file(path..'/meta_data.json') + + if not res then + nuage.err("nuageinit: error parsing config-2: meta_data.json: " .. err) + end + local obj = parser:get_object() + local sshkeys = obj["public_keys"] + if sshkeys then + local homedir = nuage.adduser(default_user) + for _,v in pairs(sshkeys) do + nuage.addsshkey(root .. homedir, v) + end + end + nuage.sethostname(obj["hostname"]) + + -- network + config2_network(path) +elseif citype == "nocloud" then + local f,err = io.open(path.."/meta-data") + if err then + nuage.err("nuageinit: error parsing nocloud meta-data: ".. err) + end + local obj = yaml.eval(f:read("*a")) + f:close() + if not obj then + nuage.err("nuageinit: error parsing nocloud meta-data") + end + local hostname = obj['local-hostname'] + if not hostname then + hostname = obj['hostname'] + end + if hostname then + nuage.sethostname(hostname) + end +else + nuage.err("Unknown cloud init type: ".. citype) +end + +-- deal with user-data +local f = io.open(path..'/user-data', "r") +if not f then + os.exit(0) +end +local line = f:read('*l') +f:close() +if line == "#cloud-config" then + f = io.open(path.."/user-data") + local obj = yaml.eval(f:read("*a")) + f:close() + if not obj then + nuage.err("nuageinit: error parsing cloud-config file: user-data") + end + if obj.groups then + for n,g in pairs(obj.groups) do + if (type(g) == "string") then + local r = nuage.addgroup({name = g}) + if not r then + nuage.warn("nuageinit: failed to add group: ".. g) + end + elseif type(g) == "table" then + for k,v in pairs(g) do + nuage.addgroup({name = k, members = v}) + end + else + nuage.warn("nuageinit: invalid type : "..type(g).." for users entry number "..n); + end + end + end + if obj.users then + for n,u in pairs(obj.users) do + if type(u) == "string" then + if u == "default" then + nuage.adduser(default_user) + else + nuage.adduser({name = u}) + end + elseif type(u) == "table" then + -- ignore users without a username + if u.name == nil then + goto unext + end + local homedir = nuage.adduser(u) + if u.ssh_authorized_keys then + for _,v in ipairs(u.ssh_authorized_keys) do + nuage.addsshkey(homedir, v) + end + end + else + nuage.warn("nuageinit: invalid type : "..type(u).." for users entry number "..n); + end + ::unext:: + end + else + -- default user if none are defined + nuage.adduser(default_user) + end + if obj.ssh_authorized_keys then + local homedir = nuage.adduser(default_user) + for _,k in ipairs(obj.ssh_authorized_keys) do + nuage.addsshkey(homedir, k) + end + end + if obj.network then + local ifaces = get_ifaces() + nuage.mkdir_p(root .. "/etc/rc.conf.d") + local network = open_config("network") + local routing = open_config("routing") + local ipv6={} + for _,v in pairs(obj.network.ethernets) do + if not v.match then goto next end + if not v.match.macaddress then goto next end + if not ifaces[v.match.macaddress] then + nuage.warn("nuageinit: not interface matching: "..v.match.macaddress) + goto next + end + local interface = ifaces[v.match.macaddress] + if v.dhcp4 then + network:write("ifconfig_"..interface.."=\"DHCP\"\n") + elseif v.addresses then + for _,a in pairs(v.addresses) do + if a:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)") then + network:write("ifconfig_"..interface.."=\"inet "..a.."\"\n") + else + network:write("ifconfig_"..interface.."_ipv6=\"inet6 "..a.."\"\n") + ipv6[#ipv6 +1] = interface + end + end + end + if v.gateway4 then + routing:write("defaultrouter=\""..v.gateway4.."\"\n") + end + if v.gateway6 then + routing:write("ipv6_defaultrouter=\""..v.gateway6.."\"\n") + routing:write("ipv6_route_"..interface.."=\""..v.gateway6) + routing:write(" -prefixlen 128 -interface "..interface.."\"\n") + end + ::next:: + end + if #ipv6 > 0 then + network:write("ipv6_network_interfaces=\"") + network:write(table.concat(ipv6, " ") .. "\"\n") + network:write("ipv6_default_interface=\""..ipv6[1].."\"\n") + end + network:close() + routing:close() + end +else + local res,err = os.execute(path..'/user-data') + if not res then + nuage.err("nuageinit: error executing user-data script: ".. err) + end +end |