mirror of
https://github.com/opnsense/src.git
synced 2026-06-06 07:12:52 -04:00
360 lines
9.4 KiB
Text
Executable file
360 lines
9.4 KiB
Text
Executable file
#!/usr/libexec/flua
|
|
---
|
|
-- SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
--
|
|
-- Copyright(c) 2022 Baptiste Daroussin <bapt@FreeBSD.org>
|
|
|
|
local nuage = require("nuage")
|
|
local ucl = require("ucl")
|
|
local yaml = require("yaml")
|
|
local sys_stat = require("posix.sys.stat")
|
|
|
|
if #arg ~= 2 then
|
|
nuage.err("Usage: " .. arg[0] .. " <cloud-init-directory> (<config-2> | <nocloud>)", false)
|
|
end
|
|
local path = arg[1]
|
|
local citype = arg[2]
|
|
|
|
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 openat(dir, name)
|
|
local path_dir = root .. dir
|
|
local path_name = path_dir .. "/" .. name
|
|
nuage.mkdir_p(path_dir)
|
|
local f, err = io.open(path_name, "w")
|
|
if not f then
|
|
nuage.err("unable to open " .. path_name .. ": " .. err)
|
|
end
|
|
return f, path_name
|
|
end
|
|
local function open_ssh_key(name)
|
|
return openat("/etc/ssh", name)
|
|
end
|
|
|
|
local function open_config(name)
|
|
return openat("/etc/rc.conf.d", name)
|
|
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("error parsing network_data.json: " .. err)
|
|
return
|
|
end
|
|
local obj = parser:get_object()
|
|
|
|
local ifaces = get_ifaces()
|
|
if not ifaces then
|
|
nuage.warn("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
|
|
|
|
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("error parsing config-2 meta_data.json: " .. err)
|
|
end
|
|
local obj = parser:get_object()
|
|
if obj.public_keys then
|
|
local homedir = nuage.adduser(default_user)
|
|
for _,v in pairs(obj.public_keys) do
|
|
nuage.addsshkey(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("error parsing nocloud meta-data: " .. err)
|
|
end
|
|
local obj = yaml.eval(f:read("*a"))
|
|
f:close()
|
|
if not obj then
|
|
nuage.err("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 ud = nil
|
|
local f = nil
|
|
userdatas = {"user-data", "user_data"}
|
|
for _, v in pairs(userdatas) do
|
|
f = io.open(path .. "/" .. v, "r")
|
|
if f then
|
|
ud = v
|
|
break
|
|
end
|
|
end
|
|
if not f then
|
|
os.exit(0)
|
|
end
|
|
local line = f:read("*l")
|
|
f:close()
|
|
if line == "#cloud-config" then
|
|
f = io.open(path .. "/" .. ud)
|
|
local obj = yaml.eval(f:read("*a"))
|
|
f:close()
|
|
if not obj then
|
|
nuage.err("error parsing cloud-config file: " .. ud)
|
|
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("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("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("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_keys and type(obj.ssh_keys) == "table" then
|
|
for key, val in pairs(obj.ssh_keys) do
|
|
for keyname, keytype in key:gmatch("(%w+)_(%w+)") do
|
|
local sshkn = nil
|
|
if keytype == "public" then
|
|
sshkn = "ssh_host_" .. keyname .. "_key.pub"
|
|
elseif keytype == "private" then
|
|
sshkn = "ssh_host_" .. keyname .. "_key"
|
|
end
|
|
if sshkn then
|
|
local sshkey, path = open_ssh_key(sshkn)
|
|
if sshkey then
|
|
sshkey:write(val .. "\n")
|
|
sshkey:close()
|
|
end
|
|
if keytype == "private" then
|
|
sys_stat.chmod(path, 384)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
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()
|
|
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("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 .. "/" .. ud)
|
|
if not res then
|
|
nuage.err("error executing user-data script: " .. err)
|
|
end
|
|
end
|