Commit Graph

46 Commits

Author SHA1 Message Date
Baptiste Daroussin 68e60bb8b6 nuageinit: expose decode_base64 to fix tests 2026-06-06 08:05:50 +02:00
Baptiste Daroussin 71e8122b3f nuage.lua: add encode_base64 helper 2026-06-06 08:01:48 +02:00
Baptiste Daroussin be711ade6f nuageinit: implement MIME multipart user-data support
Add support for MIME multipart/mixed user-data, allowing a single
user-data blob to contain multiple parts with different content types.
2026-06-05 22:45:54 +02:00
Baptiste Daroussin 4662263c24 nuageinit: implement resolv_conf support
Add support for the 'resolv_conf' cloud-config key which writes
directly to /etc/resolv.conf.
2026-06-05 13:15:37 +02:00
Baptiste Daroussin 797dad91ff nuageinit: implement mounts support
Add support for the 'mounts' cloud-config key which configures
mount points by appending entries to /etc/fstab and creating
the corresponding directories.
2026-06-05 13:15:16 +02:00
Baptiste Daroussin ba58e8ad72 nuageinit: implement manage_etc_hosts support
Add support for adding the instance hostname to /etc/hosts on the
127.0.0.1 and ::1 localhost lines, matching cloud-init's default
behaviour (manage_etc_hosts: true).

create a revolve_hostname helper to avoid code duplucation.
2026-06-05 07:49:16 +02:00
Baptiste Daroussin 22c1f5d0ec nuageinit: complete SSH support with ssh_deletekeys and disable_root
Add missing SSH cloud-config options from cloud-init spec:

- ssh_deletekeys: remove existing SSH host keys on first boot so
  new ones are generated automatically by sshd(8).
  Implemented as delete_ssh_host_keys() in nuage.lua using lfs.dir()
  with a directory existence guard via lfs.attributes().

- disable_root: set PermitRootLogin to 'no' (or a custom value via
  disable_root_opts) in /etc/ssh/sshd_config.

- disable_root_opts: optional string or array to override the
  PermitRootLogin value used when disable_root is true. Only the
  first array element is used.
2026-06-04 22:17:03 +02:00
Baptiste Daroussin ea0932d71a nuageinit: refactor goto abuse in chpasswd()
Replace goto next/list pattern with proper elseif/else control
structure. The goto-based flow was fragile and hard to follow;
the elseif chain makes the validation logic explicit and linear.
2026-06-04 22:02:58 +02:00
Baptiste Daroussin 0ba9b7b7f8 nuageinit: fix update_sshd_config crash when file does not exist
Previously update_sshd_config() would assert-fail if sshd_config did
not exist. Now it creates a new file with the given key/value.

Also replace the fragile simultaneous r+ + temp file approach with
a cleaner read-then-write pattern: read all lines into memory, modify
as needed, then write to a temp file and rename. All assert() calls
replaced with proper error handling via warnmsg().

Add test case for missing file creation.
2026-06-04 21:10:37 +02:00
Baptiste Daroussin cf5722ed60 nuageinit: fix TOCTOU in addsshkey, adddoas, addsudo
Replace check-then-create patterns with direct creation:

- addsshkey: check what exists before creation, use mkdir_p() for
  .ssh directory, handle errors with warnmsg() instead of assert().
  Apply chmod/chown only on newly created files/directories.

- adddoas: same pattern for doas.conf and the etc directory.

- addsudo: same pattern for the sudoers file and sudoers.d directory.

All three functions now use warnmsg() for error handling instead of
returning nil,err or using assert().
2026-06-04 21:06:35 +02:00
Baptiste Daroussin fdff89256f nuageinit: fix non-standard f:close(cmd) and remove dead precmd
- f:close(cmd) -> f:close() in adduser() and exec_change_password():
  the 'cmd' argument is not standard Lua and is silently ignored.
- Remove dead 'precmd' variable in adduser().
2026-06-04 20:59:30 +02:00
Baptiste Daroussin 852504a5fa nuageinit: remove dead checkgroup(), inline check in purge_group()
Call getgroups() once instead of N times per call. Inline the
membership check directly, removing the now-unused checkgroup()
helper function.
2026-06-04 20:32:48 +02:00
Baptiste Daroussin 46d1758aa7 nuageinit: add hostname validation (RFC 952/1123) to sethostname()
Validate hostnames before writing them:
- Reject empty hostnames
- Reject hostnames longer than 253 characters
- Reject hostnames with invalid characters
- Reject hostnames starting or ending with dot/hyphen
- Reject labels longer than 63 characters
- Reject labels starting or ending with hyphen

Expand the sethostname test to cover all rejection cases.
Update nuage.sh sethostname_body to ignore stderr (warnings).
2026-06-04 20:26:49 +02:00
Baptiste Daroussin 57807f389a nuageinit: add nil/empty guard to decode_base64()
Return an empty string when input is nil or zero-length instead
of processing it through the decoding loop.
2026-06-04 20:09:06 +02:00
Baptiste Daroussin b813e46e15 nuageinit: fix dirname('/') returning nil instead of '/' 2026-06-04 18:44:31 +02:00
Baptiste Daroussin 8b70a203be nuageinit: fix command injection and related issues
- Add shell_escape() helper to safely escape shell arguments
- Apply shell_escape to all user-controlled values in shell commands:
  adduser (usershow, useradd, lock, primary_group, groups)
  addgroup (groupshow, groupadd, members)
  exec_change_password (usermod)
  settimezone (tzsetup root and timezone)
  install_package (pkg package names)
- Escape double quotes in hostname when writing rc.conf.d/hostname
- Add missing 'local' declaration for resolvconf_command in nameservers()
- Escape interface name in resolvconf -a command
- Change open_resolvconf_conf() from 'w' to 'a' mode to prevent
  data loss when nameservers() is called multiple times
- Clean up stale resolvconf.conf at the start of each boot
  (skip on postnet to preserve config written by first call)

MFC After: 1 day
2026-05-12 09:52:32 +02:00
Jose Luis Duran 81af04b081 nuageinit: Silence luacheck warnings and fix typos
No functional change intended.

Reviewed by:	bapt, dtxdf, kevans
MFC after:	3 days
Differential Revision:	https://reviews.freebsd.org/D53238
2025-11-22 17:22:23 +00:00
Jesús Daniel Colmenares Oviedo 68691160f4 nuageinit: Ignore non-existent groups
In cloud-init, when a group specified in the 'users.{index}.groups' parameter
does not exist, it is ignored, but the user is created anyway. In the case of
nuageinit, it exits with an exception, since pw(8) expects each group to exist.

Reviewed by:		bapt@
Approved by:		bapt@
Differential Revision:	https://reviews.freebsd.org/D52718
2025-09-27 18:05:03 -04:00
Jesús Daniel Colmenares Oviedo 18555060dc nuageinit: Add me to copyright list
Approved by:    bapt@
2025-09-11 13:06:03 -04:00
Jesús Daniel Colmenares Oviedo 9a829e8656 nuageinit: Add doas support
* Set mode of etc directory to 0755.
* Use user.localbase sysctl instead of /usr/local.
* Add test case for doas.
* Set ${LOCALBASE} instead of /usr/local in nuageinit(7) man page.

Reviewed by:            bapt@
Approved by:            bapt@
Differential Revision:  https://reviews.freebsd.org/D52437
2025-09-11 13:06:03 -04:00
Jesús Daniel Colmenares Oviedo a5cc9b7b96 nuageinit: chmod sudoers directory instead of chmod (again) sudoers file
* Set mode of sudoers to 0440.

Reviewed by:            bapt@, jlduran@
Approved by:            bapt@, jlduran@
Differential Revision:  https://reviews.freebsd.org/D52438
2025-09-11 13:05:51 -04:00
Jesús Daniel Colmenares Oviedo ba5df7a2d0 nuageinit: Improvements for nuageinit
- Fix 'pkg update' usage:
  - The function 'nuage:run_pkg_cmd(...)' adds the flag '-y', which
    does not make sense with some commands such as 'pkg update',
    causing an error when updating the repository catalogs.
- Fix typo 'ssh-authorized-keys -> ssh_authorized_keys' in
  'nuageinit(7)' man page.
- Document 'ssh_authorized_keys' parameter.
- Use device configuration ID when no 'match' rule is specified:
  - This is the default behavior of cloud-init when no match rule is
    specified, so the device is configured anyway (even if it does not
    exist). This greatly simplifies things, since in many cases
    'if_vtnet(4)' is used, so there is no need to perform a comparison
    with the MAC address.
- Document 'network' parameter:
  - Add example to 'EXAMPLES' section.
- Set 'gateway[46]' only when 'addresses' is specified:
  - To comply with the cloud-init specification, 'gateway4' and 'gateway6'
    must only take effect when 'addresses' (or static configuration) is
    specified.
- Use a separate function to check 'match' rules:
  - This way, we can easily add new logic to new types of rules.
- Implement 'network.ethernets.{id}.match.name' parameter:
  - But unlike cloud-init, which works with glob expressions (although it
    depends on the network backend), this implementation takes advantage
    of Lua pattern-matching expressions.

    Also note that previously we were only concerned with one interface
    matching, however, to be cloud-init-compliant, we need to configure
    the matching interfaces (one or more).
- Set default router only once.
- Implement 'network.ethernets.{id}.wakeonlan' parameter.
- Implement 'network.ethernets.{id}.set-name' parameter.
- Implement 'network.ethernets.{id}.match.driver' parameter:
  - Rename 'get_ifaces(...)' function as 'get_ifaces_by_mac(...)'.
  - Add get_ifaces_by_driver(...) function.
- Implement 'network.ethernets.{id}.mtu' parameter.
- Implement 'nameservers' parameter.
- Use 'resolvconf(8)' to manipulate 'resolv.conf(5)'.
- Use 'tzsetup(8)' to set time zone.

Reviewed by:            bapt@
Approved by:            bapt@
Differential Revision:  https://reviews.freebsd.org/D51643
2025-08-22 14:40:36 -04:00
Mark Johnston 667ef8875b nuageinit: Add wrappers for chmod and chown
In the wrappers, check for errors and abort if one is raised.  At some
point it may be useful to have a mechanism to ignore errors, but I'm not
sure yet how that should look.

For chmod, let the mode be specified as an octal number, otherwise it's
hard to understand what's happening.  Note that this must be specified
as a string, otherwise tonumber() will raise an error.

Reviewed by:	bapt
MFC after:	2 weeks
Differential Revision:	https://reviews.freebsd.org/D51159
2025-07-05 14:54:07 +00:00
Baptiste Daroussin b56d2195f1 nuageinit: enhance sudo support
from the cloudinit specification sudo rules can be a string or an
array of string
2025-06-26 14:34:51 +02:00
Baptiste Daroussin 3969965c7f nuageinit: fix setting owner when only the user is set 2025-06-26 14:34:51 +02:00
Baptiste Daroussin d9a4c24df1 nuageinit: write_files fix typo breaking tests 2025-06-26 14:34:51 +02:00
Baptiste Daroussin 19a7ea3cc4 nuageinit: implement write_files
write_files is a list of files that should be created at the first boot

each file content can be either plain text or encoded in base64 (note
that cloudinit specify that gzip is supported, but we do not support it
yet.)

All other specifier from cloudinit should work:
by default all files will juste overwrite exesiting files except if
"append" is set to true, permissions, ownership can be specified.
The files are create before packages are being installed and user
created.

if "defer" is set to true then the file is being created after packages
installation and package manupulation.

This feature is requested for KDE's CI.
2025-06-26 13:47:37 +02:00
Baptiste Daroussin 6a54f886be nuageinit: add support for sudo 2025-06-15 17:18:48 +02:00
Sebastien Baylocq 682af9601a nuageinit: more package related functions
Implement package_update and package_upgrade, which allows to launch
an update of the metadata and an upgrade of the packages.

Sponsored by:	OVHCloud
2025-06-06 18:26:41 +02:00
Sebastien Baylocq 076fec1b88 nuageinit: implement packages
Installs a list of packages

Sponsored by:	OVHCloud
2025-06-06 18:26:40 +02:00
Sebastien Baylocq 43b82d69b2 nuageinit: add a function to bootstrap pkg if needed
Sponsored by:	OVHCloud
2025-06-06 18:26:38 +02:00
Baptiste Daroussin c201a1198a nuageinit: implement chpasswd
Add support for chpasswd, with all possible syntaxes, including
deprecated one: chpasswd.list as a list or as a multiline string
as some providers are still only providing this deprecated form

Sponsored by:	OVHCloud
MFC After:	1 week
Reviewed by:	kevans, jlduran
Differential Revision: 	https://reviews.freebsd.org/D50021
2025-04-30 09:32:06 +02:00
Baptiste Daroussin 18d74dc0cd nuageinit: fix luacheck nit
Reported by:	jlduran
2025-04-23 16:49:46 +02:00
Baptiste Daroussin f85d086827 nuageinint: implement ssh_pwauth
ssh_pwauth sets the value in sshd_config for the password authentication
This implementation tries to avoid touching the file if cloudinit
request for what is already the default value.

MFC After:	3 days
Sponsored by:	OVHCloud
Reviewed by:	kevans, jlduran
Differential Revision:	https://reviews.freebsd.org/D49875
2025-04-23 16:29:02 +02:00
Baptiste Daroussin 3e50286607 nuageinit: use io.popen instead of pipes in shell for password
using echo in a sh(1) command line, requires many escaping to be done
right, using io.popen we don't need to do this escaping anymore.
2024-11-20 10:41:46 +01:00
Jose Luis Duran b9ce743c54 nuageinit: Fix passwords
The hashed password usually contains a "$" sign, which, when used on a
shell, must be escaped.  Also, the plain text password may contain
special characters that require escaping.

Add a quick fix by enclosing it in single quotes.  Note that if the
plain text password contains a "'", it will still fail.  This will be
properly fixed in later commits.

Some here documents require the document to be a string literal,
especially when passing invalid characters.  Enclose it in single
quotes.

Signed-off-by: Jose Luis Duran <jlduran@gmail.com>
2024-09-26 12:59:56 +02:00
Jose Luis Duran 945632ca76 nuageinit: Standardize warning/error messages
Standardize the utilities from nuage.lua, to return nil on failure, plus
an error message as a second result, and some value different from nil
on success.

Make warnmsg() and errmsg() append "nuageinit: " by default.  Pass an
optional second parameter as false to avoid printing this tag.

Signed-off-by: Jose Luis Duran <jlduran@gmail.com>
2024-09-26 12:59:14 +02:00
Jose Luis Duran 9b2d92addc nuageinit: Replace os.execute with Lua libraries
Prefer posix.sys.stat's chmod() to os.execute().  While here, change the
name of the locals to be more descriptive.

Signed-off-by: Jose Luis Duran <jlduran@gmail.com>
2024-09-26 12:59:00 +02:00
Jose Luis Duran 504981357a nuageinit: Lua check and lint files
Mostly white space, style, and luacheck compliance.

Signed-off-by: Jose Luis Duran <jlduran@gmail.com>
2024-09-26 12:58:00 +02:00
Baptiste Daroussin a6ecbf2b35 nuageinit: improve debugging when mkdir fails 2024-08-20 12:09:43 +02:00
Jose Luis Duran 7aecd689e3 nuageinit: Fix the homedir variable name
cloud-init uses homedir, not home.
2024-07-25 11:15:29 +02:00
Jose Luis Duran 07d17ca189 nuageinit: Set recommended SSH permissions
As stated in sshd(8), the recommended permissions for ~/.ssh are
read/write/execute for the user, and not accessible by others; and the
recommended permissions for ~/.ssh/authorized_keys are read/write for
the user, and not accessible by others.
2024-07-23 15:02:28 +02:00
Jose Luis Duran 7b73ecfe64 nuageinit: Accept plain text passwords
Per pw(8), when -H is set, the password should be supplied already
encrypted in a form suitable for writing directly to the password
database (passwd in cloud-init tems); -h provides a special interface by
which interactive scripts can set an account password using pw(8) in
plain text (plain_text_passwd in cloud-init terms).

The default user (freebsd) is defined with a plain_text_passwd
(freebsd), not with an encrypted one.
2024-07-23 15:01:54 +02:00
Baptiste Daroussin fa07b02f6e nuageinit: make addsshkey friendly for testsuite 2024-06-05 11:00:53 +02:00
Baptiste Daroussin 83fcab792c nuageinit: use pw(8) instead of getent(1)
pw(8) allows to seek for users in a custom rootdir, which makes it
easier for a testsuite

MFC After:	3 days
2024-06-05 11:00:53 +02:00
Baptiste Daroussin a42d6f7601 nuageinit: add basic support for cloudinit.
this is a very early script to support cloudinit, it does not intend to
be a full featured cloudinit client, but will support a good enough
subset to be viable in most case.

It support nocloud and openstack config-2 config drive mode (iso9660 or
msdosfs)

The following features are currently supported:
- adding users (including a default user named 'freebsd' with password
  'freebsd'
- adding groups
- adding ssh keys
- static ipv4, static ipv6, dynamic ipv4

With this one is able to use the 'bring your own image feature" out of
box.

It is expected that the script grows the support of other clouds
supporting cloud-init, contributions are welcomed.

It is designed to be only run once via the firstboot mecanism.

Sponsored by:	OVHCloud
MFC After:	3 weeks
Differential Revision:	https://reviews.freebsd.org/D44141
2024-03-15 09:22:16 +01:00