diff --git a/libexec/Makefile b/libexec/Makefile --- a/libexec/Makefile +++ b/libexec/Makefile @@ -26,6 +26,7 @@ ${_rshd} \ ${_rtld-elf} \ save-entropy \ + ${_simplecloudinit} \ ${_smrsh} \ ${_tests} \ ${_tftp-proxy} \ @@ -112,6 +113,10 @@ _tests= tests .endif +.if ${MK_SIMPLECLOUDINIT} != "no" +_simplecloudinit= simplecloudinit +.endif + .include .include diff --git a/libexec/rc/rc.d/Makefile b/libexec/rc/rc.d/Makefile --- a/libexec/rc/rc.d/Makefile +++ b/libexec/rc/rc.d/Makefile @@ -296,6 +296,12 @@ SMRCDPACKAGE= sendmail .endif +.if ${MK_SIMPLECLOUDINIT} != "no" +CONFGROUPS+= SCI +SCI= simplecloudinit +SCIPACKAGE= simplecloutinit +.endif + .if ${MK_UNBOUND} != "no" CONFGROUPS+= UNBOUND UNBOUND+= local_unbound diff --git a/libexec/rc/rc.d/simplecloudinit b/libexec/rc/rc.d/simplecloudinit new file mode 100755 --- /dev/null +++ b/libexec/rc/rc.d/simplecloudinit @@ -0,0 +1,36 @@ +#!/bin/sh +# +# $FreeBSD$ +# + +# PROVIDE: simplecloudinit +# REQUIRE: mountcritlocal +# BEFORE: NETWORKING +# KEYWORD: firstboot + +. /etc/rc.subr + +name="simplecloudinit" +desc="Simple Cloud Init configuration" +start_cmd="simplecloudinit_start" +stop_cmd=":" +rcvar="simplecloudinit_enable" + +simplecloudinit_start() +{ + # detect cloud init provider + # ovh do expect a drive called config-drive + if [ -e /dev/gpt/config-drive ]; then + fs=$(fstyp /dev/gpt/config-drive) + mkdir -p /media/cloudinit + mount -t $fs /dev/gpt/config-drive /media/cloudinit + /usr/libexec/simplecloudinit /media/cloudinit/openstack/latest/ + umount /media/cloudinit + rmdir /media/cloudinit + else + err 1 "Impossible to find a cloud init provider" + fi +} + +load_rc_config $name +run_rc_command "$1" diff --git a/libexec/simplecloudinit/Makefile b/libexec/simplecloudinit/Makefile new file mode 100644 --- /dev/null +++ b/libexec/simplecloudinit/Makefile @@ -0,0 +1,6 @@ +#! $FreeBSD$ + +PACKAGES= simplecloudinit +SCRIPTS= simplecloudinit + +.include diff --git a/libexec/simplecloudinit/simplecloudinit b/libexec/simplecloudinit/simplecloudinit new file mode 100755 --- /dev/null +++ b/libexec/simplecloudinit/simplecloudinit @@ -0,0 +1,137 @@ +#!/usr/libexec/flua + +-- SPDX-License-Identifier: BSD-2-Clause-FreeBSD +-- +-- Copyright(c) 2022 Baptiste Daroussin +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions +-- are met: +-- 1. Redistributions of source code must retain the above copyright +-- notice, this list of conditions and the following disclaimer. +-- 2. Redistributions in binary form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +-- SUCH DAMAGE. + +-- $FreeBSD$ + +if #arg ~= 1 then + print("Usage ".. arg[0] .." ") + os.exit(1) +end +path = arg[1] +local ucl = require("ucl") + +local parser = ucl.parser() +local res,err = parser:parse_file(path..'/meta_data.json') + +if not res then + print("Error parsing meta_data.json: " .. err) + os.exit(1) +end +local f = io.popen("getent passwd freebsd") +local pwd = f:read("*a") +local homedir = '/home/freebsd' +if pwd:len() == 0 then + r = os.execute("pw useradd freebsd -m -M 0755 -w none -n freebsd -G 0 -c 'FreeBSD User' -d '" .. homedir .."' -s '/bin/sh'") +else + homedir = pwd:match("%a+:.+:%d+:%d+:.*:(.*):.*") +end +local obj = parser:get_object() +local sshkeys = obj["public_keys"] +if sshkeys then + for _,v in pairs(sshkeys) do + lfs.mkdir("/home/freebsd/.ssh") + f = io.open("/home/freebsd/.ssh/authorized_keys", "w+") + f:write(v .. "\n") + f:close() + end + os.execute("chown -R freebsd:freebsd /home/freebsd/.ssh") +end +local hostname = obj["hostname"] +if hostname then + f = io.open("/etc/rc.conf.d/hostname", "w") + f:write("hostname=\""..hostname.."\"\n") + f:close() +end + +-- network +parser = ucl.parser() +local res,err = parser:parse_file(path..'/network_data.json') +if not res then + print("Error parsing network_data.json: " .. err) + os.exit(1) +end +obj = parser:get_object() + +-- grab ifaces +local ns = io.popen('netstat -i --libxo json') +local netres = ns:read("*a") +ns:close() +parser = ucl.parser() +local res,err = parser:parse_string(netres) +local ifaces = parser:get_object() + +local myifaces = {} +for _,iface in pairs(ifaces["statistics"]["interface"]) do + for o in iface["network"]:gmatch("") do + local s = iface["address"]:lower() + myifaces[s] = iface["name"] + end +end + +local mylinks = {} +for _,v in pairs(obj["links"]) do + local s = v["ethernet_mac_address"]:lower() + mylinks[v["id"]] = myifaces[s] +end + +local netif = io.open("/etc/rc.conf.d/netif", "w") +local routing = io.open("/etc/rc.conf.d/routing", "w") +local ipv6 = {} +for _,v in pairs(obj["networks"]) do + if v["type"] == "ipv4_dhcp" then + netif:write("ifconfig_"..mylinks[v["link"]].."=\"DHCP\"\n") + end + if v["type"] == "ipv6" then + table.insert(ipv6, mylinks[v["link"]]) + netif:write("ifconfig_"..mylinks[v["link"]].."_ipv6=\"inet6 "..v["ip_address"].." prefixlen 128 accept_rtadv no_radr\"\n") + routing:write("ipv6_defaultrouter=\""..v["gateway"].."\"\n") + routing:write("ipv6_route_"..mylinks[v["link"]].."=\""..v["gateway"].." -prefixlen 128 -interface "..mylinks[v["link"]].."\"\n") + if v["route"] then + for _,r in v["route"] do + -- 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 + print("Not implemented yet") + os.exit(1) + ::next:: + end + end + end +end +netif:write("ipv6_network_interfaces=\"") +for _,v in pairs(ipv6) do + netif:write(v.. " ") +end +netif:write("\"\n") +routing:write("ipv6_static_routes=\"") +for _,v in pairs(ipv6) do + routing:write(v.. " ") +end +routing:write("\"\n") +netif:write("ipv6_default_interface=\""..ipv6[1].."\"\n") +netif:close() +routing:close() diff --git a/share/mk/src.opts.mk b/share/mk/src.opts.mk --- a/share/mk/src.opts.mk +++ b/share/mk/src.opts.mk @@ -169,6 +169,7 @@ SENDMAIL \ SERVICESDB \ SETUID_LOGIN \ + SIMPLECLOUDINIT \ SHARED_TOOLCHAIN \ SHAREDOCS \ SOURCELESS \ diff --git a/tools/build/mk/OptionalObsoleteFiles.inc b/tools/build/mk/OptionalObsoleteFiles.inc --- a/tools/build/mk/OptionalObsoleteFiles.inc +++ b/tools/build/mk/OptionalObsoleteFiles.inc @@ -7962,6 +7962,11 @@ OLD_FILES+=var/db/services.db .endif +.if ${MK_SIMPLECLOUDINIT} == no +OLD_FILES+=etc/rc.d/simplecloudinit +OLD_FILES+=usr/libexec/simplecloudinit +.endif + .if ${MK_SHAREDOCS} == no OLD_FILES+=usr/share/doc/pjdfstest/README OLD_DIRS+=usr/share/doc/pjdfstest