396 lines
9.4 KiB
Plaintext
396 lines
9.4 KiB
Plaintext
# Mgmt
|
|
# Copyright (C) James Shubin and the project contributors
|
|
# Written by James Shubin <james@shubin.ca> and the project contributors
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
#
|
|
# Additional permission under GNU GPL version 3 section 7
|
|
#
|
|
# If you modify this program, or any covered work, by linking or combining it
|
|
# with embedded mcl code and modules (and that the embedded mcl code and
|
|
# modules which link with this program, contain a copy of their source code in
|
|
# the authoritative form) containing parts covered by the terms of any other
|
|
# license, the licensors of this program grant you additional permission to
|
|
# convey the resulting work. Furthermore, the licensors of this program grant
|
|
# the original author, James Shubin, additional permission to update this
|
|
# additional permission if he deems it necessary to achieve the goals of this
|
|
# additional permission.
|
|
|
|
import "fmt"
|
|
import "golang"
|
|
import "golang/strings" as golang_strings
|
|
import "iter"
|
|
import "local"
|
|
import "net"
|
|
import "os"
|
|
import "strings"
|
|
import "world/collect"
|
|
|
|
# ssh_keygen creates an ssh key pair in the user's home directory if the private
|
|
# key doesn't exist.
|
|
# TODO: add more parameters such as key size and type in the future
|
|
class ssh_keygen($user) {
|
|
panic($user == "") # panic if $user is empty
|
|
$p = os.expand_home("~${user}/") # eg: ~james/
|
|
exec "ssh-keygen-${user}" {
|
|
cmd => "/usr/bin/ssh-keygen",
|
|
args => [
|
|
"-t", "rsa", # type
|
|
"-f", "${p}.ssh/id_rsa", # private key file
|
|
"-N", "", # empty password
|
|
],
|
|
creates => "${p}.ssh/id_rsa",
|
|
user => $user,
|
|
|
|
Before => File["${p}.ssh/id_rsa"],
|
|
}
|
|
# This also serves as a "handle" so that other resources can depend on
|
|
# this file getting created before they run.
|
|
file "${p}.ssh/" {
|
|
state => "exists",
|
|
mode => "u=rwx,go=",
|
|
owner => $user,
|
|
}
|
|
file "${p}.ssh/id_rsa" {
|
|
mode => "u=rw,go=",
|
|
owner => $user,
|
|
}
|
|
|
|
line "${user}@${hostname}" {
|
|
#file => "", # specified on collect
|
|
#state => "exists", # specified on collect
|
|
content => os.readfilewait("${p}.ssh/id_rsa.pub"),
|
|
|
|
Meta:hidden => true,
|
|
Meta:export => ["*",],
|
|
}
|
|
}
|
|
|
|
# ssh_authorized_keys pulls down an export key for a $user, from a user@host.
|
|
class ssh_authorized_keys($user, $from) {
|
|
panic($user == "") # panic if $user is empty
|
|
$p = os.expand_home("~${user}/") # eg: ~james/
|
|
|
|
$all = collect.res("line") # []struct{name str; host str;}
|
|
$fn = func($st) {
|
|
$st->name == "${from}" and $st->host != "${hostname}"
|
|
}
|
|
$filtered = iter.filter($all, $fn)
|
|
|
|
collect line $filtered { # pull down everyone's public keys
|
|
file => "${p}.ssh/authorized_keys",
|
|
state => $const.res.file.state.exists,
|
|
|
|
Meta:hidden => false,
|
|
}
|
|
}
|
|
|
|
# network_rename takes the device with the $mac address and renames it to $dev.
|
|
class network_rename($mac, $dev) {
|
|
#panic(not net.is_mac("${mac}"))
|
|
|
|
file "/etc/systemd/network/70-rename-${dev}.link" {
|
|
state => "exists",
|
|
content => "
|
|
# Pick the device name based on the mac address.
|
|
[Match]
|
|
MACAddress=${mac}
|
|
|
|
[Link]
|
|
Name=${dev}
|
|
",
|
|
mode => "u=rw,go=r",
|
|
owner => "root",
|
|
group => "root",
|
|
|
|
Notify => Exec["udevadm trigger"],
|
|
}
|
|
|
|
# TODO: we only want to run this once, but it's harmless for now
|
|
exec "udevadm trigger" {
|
|
cmd => "/usr/sbin/udevadm trigger --type=all --action=add --prioritized-subsystem=net --settle",
|
|
}
|
|
}
|
|
|
|
# networkd_dhcp sets up a dhcp client with systemd-networkd.
|
|
class networkd_dhcp($dev, $st) {
|
|
$dns = $st->dns || ["8.8.8.8",]
|
|
|
|
$tmpl =
|
|
"
|
|
[Match]
|
|
Name=${dev}
|
|
|
|
[Network]
|
|
DHCP=yes
|
|
{{ range .dns -}}
|
|
DNS={{ . }}
|
|
{{ end -}}
|
|
#UseGateway=false
|
|
|
|
[DHCP]
|
|
UseDNS=false
|
|
RouteMetric=100
|
|
"
|
|
|
|
$args = struct{
|
|
#dev => $dev,
|
|
dns => $dns,
|
|
}
|
|
|
|
file "/etc/systemd/network/${dev}-dhcp.network" {
|
|
state => "exists",
|
|
content => golang.template($tmpl, $args),
|
|
mode => "u=rw,go=r",
|
|
owner => "root",
|
|
group => "root",
|
|
|
|
Notify => Svc["systemd-networkd"],
|
|
}
|
|
|
|
svc "systemd-networkd" {
|
|
state => "running",
|
|
startup => "enabled",
|
|
}
|
|
}
|
|
|
|
# networkd_static sets up a static ip address with systemd-networkd.
|
|
class networkd_static($dev, $st) {
|
|
$cidr = $st->cidr # cidr
|
|
$ip = net.cidr_to_ip($cidr)
|
|
$prefix = net.cidr_to_prefix($cidr)
|
|
#$router = $st->router || ""
|
|
$dns = $st->dns || ["8.8.8.8",]
|
|
#$vips []str = $st->vips || [] # []cidr
|
|
|
|
$tmpl =
|
|
"
|
|
[Match]
|
|
Name=${dev}
|
|
|
|
[Network]
|
|
Address=${ip}/${prefix}
|
|
{{ range .dns -}}
|
|
DNS={{ . }}
|
|
{{ end -}}
|
|
#UseGateway=false
|
|
|
|
[Address]
|
|
RouteMetric=101
|
|
|
|
[Route]
|
|
Metric=1001
|
|
#Gateway=
|
|
#Destination=0.0.0.0/0
|
|
"
|
|
|
|
$args = struct{
|
|
#dev => $dev,
|
|
dns => $dns,
|
|
}
|
|
|
|
file "/etc/systemd/network/${dev}-static.network" {
|
|
state => "exists",
|
|
content => golang.template($tmpl, $args),
|
|
mode => "u=rw,go=r",
|
|
owner => "root",
|
|
group => "root",
|
|
|
|
Notify => Svc["systemd-networkd"],
|
|
}
|
|
|
|
svc "systemd-networkd" {
|
|
state => "running",
|
|
startup => "enabled",
|
|
}
|
|
}
|
|
|
|
# network_manager_dhcp sets up a dhcp client with network manager.
|
|
# NOTE: To see what it's using run: `nmcli -f name,uuid,filename connection`.
|
|
class network_manager_dhcp($st) {
|
|
$uuid = $st->uuid || "" # 01234567-89ab-cdef-0123-456789abcdef
|
|
$mac = $st->mac || ""
|
|
$dev = $st->dev || "eth0"
|
|
|
|
$dns = $st->dns || ["8.8.8.8",]
|
|
$dns_str = golang_strings.join($dns, ";") # the line also ends with a semicolon
|
|
$tmpl =
|
|
"
|
|
[connection]
|
|
id=${dev}
|
|
{{ if .uuid -}}
|
|
uuid={{ .uuid }}
|
|
{{ end -}}
|
|
type=ethernet
|
|
interface-name=${dev}
|
|
autoconnect=true
|
|
|
|
[ipv4]
|
|
{{ if .dns -}}
|
|
dns=${dns_str};
|
|
{{ end -}}
|
|
dns-search=
|
|
may-fail=false
|
|
method=auto
|
|
|
|
[ethernet]
|
|
{{ if .mac -}}
|
|
mac-address={{ .mac }}
|
|
{{ end -}}
|
|
"
|
|
|
|
$args = struct{
|
|
uuid => $uuid,
|
|
mac => $mac,
|
|
dns => $dns,
|
|
}
|
|
|
|
file "/etc/NetworkManager/system-connections/${dev}.nmconnection" {
|
|
state => "exists",
|
|
content => golang.template($tmpl, $args),
|
|
mode => "u=rw,go=",
|
|
owner => "root",
|
|
|
|
Notify => Svc["NetworkManager"],
|
|
}
|
|
|
|
svc "NetworkManager" {
|
|
}
|
|
}
|
|
|
|
# network_manager_static sets up a static ip address with network manager.
|
|
# NOTE: To see what it's using run: `nmcli -f name,uuid,filename connection`.
|
|
class network_manager_static($st) {
|
|
$uuid = $st->uuid || "" # 01234567-89ab-cdef-0123-456789abcdef
|
|
$mac = $st->mac || ""
|
|
$dev = $st->dev || "eth0"
|
|
$cidr = $st->cidr # cidr
|
|
$ip = net.cidr_to_ip($cidr)
|
|
$prefix = net.cidr_to_prefix($cidr)
|
|
$router = $st->router || ""
|
|
$dns = $st->dns || ["8.8.8.8",]
|
|
$vips []str = $st->vips || [] # []cidr
|
|
|
|
$dns_str = golang_strings.join($dns, ";") # the line also ends with a semicolon
|
|
$tmpl =
|
|
"
|
|
[connection]
|
|
id=${dev}
|
|
{{ if .uuid -}}
|
|
uuid={{ .uuid }}
|
|
{{ end -}}
|
|
type=ethernet
|
|
interface-name=${dev}
|
|
autoconnect=true
|
|
|
|
[ipv4]
|
|
{{ if .router -}}
|
|
address1=${ip}/${prefix},{{ .router }}
|
|
{{ else -}}
|
|
address1=${ip}/${prefix}
|
|
{{ end -}}
|
|
{{ if .vips -}}
|
|
{{ range $index, $ip := .vips }}
|
|
{{ $ix := len (printf \"xx%*s\" $index \"\") -}}
|
|
address{{ $ix }}={{ $ip }}
|
|
{{ end -}}
|
|
{{ end -}}
|
|
{{ if .dns -}}
|
|
dns=${dns_str};
|
|
{{ end -}}
|
|
dns-search=
|
|
may-fail=false
|
|
method=manual
|
|
|
|
[ethernet]
|
|
{{ if .mac -}}
|
|
mac-address={{ .mac }}
|
|
{{ end -}}
|
|
"
|
|
|
|
$args = struct{
|
|
uuid => $uuid,
|
|
mac => $mac,
|
|
router => $router,
|
|
dns => $dns,
|
|
vips => $vips,
|
|
}
|
|
|
|
file "/etc/NetworkManager/system-connections/${dev}.nmconnection" {
|
|
state => "exists",
|
|
content => golang.template($tmpl, $args),
|
|
mode => "u=rw,go=",
|
|
owner => "root",
|
|
|
|
Notify => Svc["NetworkManager"],
|
|
}
|
|
|
|
svc "NetworkManager" {
|
|
}
|
|
}
|
|
|
|
# systemd_daemon_reload creates an exec which runs a systemctl daemon-reload if
|
|
# one is needed. The exec resource uses the input name, to make it easy to
|
|
# attach an edge to it.
|
|
# TODO: can we use: systemctl show foo.service --property=NeedDaemonReload
|
|
class systemd_daemon_reload($name) {
|
|
$vardir = local.vardir("misc/")
|
|
$mtime_file = "${vardir}daemon-reload"
|
|
|
|
exec "${name}" {
|
|
cmd => "/usr/bin/systemctl",
|
|
args => [
|
|
"daemon-reload",
|
|
],
|
|
|
|
donecmd => "/usr/bin/date --utc > ${mtime_file}",
|
|
doneshell => "/usr/bin/bash",
|
|
|
|
watchfiles => [
|
|
"/lib/systemd/system/",
|
|
"/etc/systemd/system/",
|
|
"${mtime_file}",
|
|
],
|
|
|
|
# If we have any file that's newer than our mtime, the run.
|
|
# NOTE: We grep since find doesn't return a useful return code.
|
|
ifcmd => "/usr/bin/test ! -e '${mtime_file}' || /usr/bin/find /lib/systemd/system/ /etc/systemd/system/ -type f -name '*.service' -newer '${mtime_file}' | /usr/bin/grep -q .",
|
|
ifshell => "/usr/bin/bash",
|
|
}
|
|
}
|
|
|
|
# Instead of running `dnf copr enable purpleidea/foo` you can use
|
|
# `purpleidea/foo` as the $name here to accomplish the same thing.
|
|
class copr_enable($name) {
|
|
# example: sudo dnf copr enable gsauthof/dracut-sshd
|
|
# /etc/yum.repos.d/_copr:copr.fedorainfracloud.org:gsauthof:dracut-sshd.repo
|
|
$sp = strings.split($name, "/")
|
|
$user = $sp[0]
|
|
$repo = $sp[1]
|
|
$p = fmt.printf("/etc/yum.repos.d/_copr:copr.fedorainfracloud.org:%s:%s.repo", $user, $repo)
|
|
|
|
# TODO: If we wanted to be more thorough, we could template the file
|
|
# instead or running this copr enable command.
|
|
exec "dnf-copr-enable-${name}" {
|
|
cmd => "/usr/bin/dnf",
|
|
args => [
|
|
"copr",
|
|
"enable",
|
|
"${name}",
|
|
],
|
|
creates => $p,
|
|
user => "root",
|
|
}
|
|
}
|