Skip to content

Commit 5b83d3c

Browse files
committed
fcos/v1_6_exp: Add new sugar for Selinux Modules.
1 parent 0a1b18e commit 5b83d3c

File tree

6 files changed

+414
-0
lines changed

6 files changed

+414
-0
lines changed

config/common/errors.go

+5
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ var (
9393

9494
// Kernel arguments
9595
ErrGeneralKernelArgumentSupport = errors.New("kernel argument customization is not supported in this spec version")
96+
97+
// Selinux Module
98+
ErrContentInvalid = errors.New("Content is empty, please provide content.")
99+
ErrNameInvalid = errors.New("Name is empty, please provide a valid name.")
100+
ErrFieldInvalid = errors.New("Please, provide valid information.")
96101
)
97102

98103
type ErrUnmarshal struct {

config/fcos/v1_6_exp/schema.go

+10
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type Config struct {
2222
base.Config `yaml:",inline"`
2323
BootDevice BootDevice `yaml:"boot_device"`
2424
Grub Grub `yaml:"grub"`
25+
Selinux Selinux `yaml:"selinux"`
2526
}
2627

2728
type BootDevice struct {
@@ -49,3 +50,12 @@ type GrubUser struct {
4950
Name string `yaml:"name"`
5051
PasswordHash *string `yaml:"password_hash"`
5152
}
53+
54+
type Selinux struct {
55+
Module []Module `yaml:"module"`
56+
}
57+
58+
type Module struct {
59+
Name string `yaml:"name"`
60+
Content string `yaml:"content"`
61+
}

config/fcos/v1_6_exp/translate.go

+72
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package v1_6_exp
1616

1717
import (
1818
"fmt"
19+
"os/exec"
1920
"strings"
2021

2122
baseutil "github.com/coreos/butane/base/util"
@@ -89,6 +90,13 @@ func (c Config) ToIgn3_5Unvalidated(options common.TranslateOptions) (types.Conf
8990
retConfig, ts := baseutil.MergeTranslatedConfigs(retp, tsp, ret, ts)
9091
ret = retConfig.(types.Config)
9192
r.Merge(rp)
93+
94+
// Clean this as it needs to not be so confusing
95+
retr, trs, rr := c.handleSelinux(options)
96+
returnConfig, ts := baseutil.MergeTranslatedConfigs(retr, trs, ret, ts)
97+
ret = returnConfig.(types.Config)
98+
r.Merge(rr)
99+
92100
return ret, ts, r
93101
}
94102

@@ -367,3 +375,67 @@ func buildGrubConfig(gb Grub) string {
367375
superUserCmd := fmt.Sprintf("set superusers=\"%s\"\n", strings.Join(allUsers, " "))
368376
return "# Generated by Butane\n\n" + superUserCmd + strings.Join(cmds, "\n") + "\n"
369377
}
378+
379+
func (c Config) handleSelinux(options common.TranslateOptions) (types.Config, translate.TranslationSet, report.Report) {
380+
rendered := types.Config{}
381+
ts := translate.NewTranslationSet("yaml", "json")
382+
var r report.Report
383+
384+
for i, module := range c.Selinux.Module {
385+
yamlPath := path.New("yaml", "selinux", "module", i)
386+
if module.Name != "" && module.Content != "" {
387+
rendered = processModule(rendered, module, options, ts, r, yamlPath)
388+
break
389+
} else {
390+
r.AddOnWarn(path.New("yaml", "selinux", "module", i), common.ErrFieldInvalid)
391+
}
392+
}
393+
394+
if len(rendered.Storage.Files) > 0 {
395+
rendered.Storage.Filesystems = append(rendered.Storage.Filesystems,
396+
types.Filesystem{
397+
Device: "/dev/disk/by-label/boot",
398+
Format: util.StrToPtr("ext4"),
399+
Path: util.StrToPtr("/boot"),
400+
})
401+
}
402+
403+
return rendered, ts, r
404+
}
405+
406+
func processModule(rendered types.Config, module Module, options common.TranslateOptions, ts translate.TranslationSet, r report.Report, yamlPath path.ContextPath) types.Config {
407+
src, compression, err := baseutil.MakeDataURL([]byte(module.Content), nil, !options.NoResourceAutoCompression)
408+
if err != nil {
409+
r.AddOnError(yamlPath, err)
410+
return rendered
411+
}
412+
filePath := fmt.Sprintf("/etc/selinux/targeted/modules/active/extra/%s.cil", module.Name)
413+
414+
rendered.Storage.Files = append(rendered.Storage.Files,
415+
types.File{
416+
Node: types.Node{
417+
Path: filePath,
418+
},
419+
FileEmbedded1: types.FileEmbedded1{
420+
Append: []types.Resource{
421+
{
422+
Source: util.StrToPtr(src),
423+
Compression: compression,
424+
},
425+
},
426+
},
427+
})
428+
429+
commandToExecute := "semodule -i"
430+
cmd := exec.Command(commandToExecute, filePath)
431+
err = cmd.Run()
432+
if err != nil {
433+
fmt.Printf("Error running semodule %v", module.Name)
434+
}
435+
436+
fmt.Printf("SELinux module file imported successfully\n")
437+
438+
ts.AddFromCommonSource(yamlPath, path.New("json", "storage"), rendered.Storage)
439+
440+
return rendered
441+
}

config/fcos/v1_6_exp/translate_test.go

+264
Original file line numberDiff line numberDiff line change
@@ -1637,3 +1637,267 @@ func TestTranslateGrub(t *testing.T) {
16371637
})
16381638
}
16391639
}
1640+
1641+
func TestTranslateSelinux(t *testing.T) {
1642+
translations := []translate.Translation{
1643+
{From: path.New("yaml", "version"), To: path.New("json", "ignition", "version")},
1644+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage")},
1645+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "filesystems")},
1646+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "filesystems", 0)},
1647+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "filesystems", 0, "path")},
1648+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "filesystems", 0, "device")},
1649+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "filesystems", 0, "format")},
1650+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "files")},
1651+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "files", 0)},
1652+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "files", 0, "path")},
1653+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "files", 0, "append")},
1654+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "files", 0, "append", 0)},
1655+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "files", 0, "append", 0, "source")},
1656+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "files", 0, "append", 0, "compression")},
1657+
}
1658+
tests := []struct {
1659+
in Config
1660+
out types.Config
1661+
exceptions []translate.Translation
1662+
report report.Report
1663+
}{
1664+
// config with one module
1665+
{
1666+
Config{
1667+
Selinux: Selinux{
1668+
Module: []Module{
1669+
{
1670+
Name: "some_name",
1671+
Content: "some content here",
1672+
},
1673+
},
1674+
},
1675+
},
1676+
types.Config{
1677+
Ignition: types.Ignition{
1678+
Version: "3.5.0-experimental",
1679+
},
1680+
Storage: types.Storage{
1681+
Filesystems: []types.Filesystem{
1682+
{
1683+
Device: "/dev/disk/by-label/boot",
1684+
Format: util.StrToPtr("ext4"),
1685+
Path: util.StrToPtr("/boot"),
1686+
},
1687+
},
1688+
Files: []types.File{
1689+
{
1690+
Node: types.Node{
1691+
Path: "/etc/selinux/targeted/modules/active/extra/some_name.cil",
1692+
},
1693+
FileEmbedded1: types.FileEmbedded1{
1694+
Append: []types.Resource{
1695+
{
1696+
Source: util.StrToPtr("data:,some%20content%20here"),
1697+
Compression: util.StrToPtr(""),
1698+
},
1699+
},
1700+
},
1701+
},
1702+
},
1703+
},
1704+
},
1705+
translations,
1706+
report.Report{},
1707+
},
1708+
}
1709+
1710+
for i, test := range tests {
1711+
t.Run(fmt.Sprintf("translate %d", i), func(t *testing.T) {
1712+
actual, translations, r := test.in.ToIgn3_5Unvalidated(common.TranslateOptions{})
1713+
r = confutil.TranslateReportPaths(r, translations)
1714+
baseutil.VerifyReport(t, test.in, r)
1715+
assert.Equal(t, test.out, actual, "translation mismatch")
1716+
assert.Equal(t, test.report, r, "report mismatch")
1717+
baseutil.VerifyTranslations(t, translations, test.exceptions)
1718+
assert.NoError(t, translations.DebugVerifyCoverage(actual), "incomplete TranslationSet coverage")
1719+
})
1720+
}
1721+
}
1722+
1723+
func TestTranslateSelinuxaaa(t *testing.T) {
1724+
translations := []translate.Translation{
1725+
{From: path.New("yaml", "version"), To: path.New("json", "ignition", "version")},
1726+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage")},
1727+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "filesystems")},
1728+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "filesystems", 0)},
1729+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "filesystems", 0, "path")},
1730+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "filesystems", 0, "device")},
1731+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "filesystems", 0, "format")},
1732+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "files")},
1733+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "files", 0)},
1734+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "files", 0, "path")},
1735+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "files", 0, "append")},
1736+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "files", 0, "append", 0)},
1737+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "files", 0, "append", 0, "source")},
1738+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "files", 0, "append", 0, "compression")},
1739+
}
1740+
1741+
// With one module
1742+
in := Config{
1743+
Selinux: Selinux{
1744+
Module: []Module{
1745+
{
1746+
Name: "some_name",
1747+
Content: "some content here",
1748+
},
1749+
},
1750+
},
1751+
}
1752+
out := types.Config{
1753+
Ignition: types.Ignition{
1754+
Version: "3.5.0-experimental",
1755+
},
1756+
Storage: types.Storage{
1757+
Filesystems: []types.Filesystem{
1758+
{
1759+
Device: "/dev/disk/by-label/boot",
1760+
Format: util.StrToPtr("ext4"),
1761+
Path: util.StrToPtr("/boot"),
1762+
},
1763+
},
1764+
Files: []types.File{
1765+
{
1766+
Node: types.Node{
1767+
Path: "/etc/selinux/targeted/modules/active/extra/some_name.cil",
1768+
},
1769+
FileEmbedded1: types.FileEmbedded1{
1770+
Append: []types.Resource{
1771+
{
1772+
Source: util.StrToPtr("data:,some%20content%20here"),
1773+
Compression: util.StrToPtr(""),
1774+
},
1775+
},
1776+
},
1777+
},
1778+
},
1779+
},
1780+
}
1781+
1782+
test := struct {
1783+
in Config
1784+
out types.Config
1785+
exceptions []translate.Translation
1786+
report report.Report
1787+
}{
1788+
in,
1789+
out,
1790+
translations,
1791+
report.Report{},
1792+
}
1793+
1794+
t.Run(fmt.Sprintf("translate %d", 0), func(t *testing.T) {
1795+
actual, translations, r := test.in.ToIgn3_5Unvalidated(common.TranslateOptions{})
1796+
r = confutil.TranslateReportPaths(r, translations)
1797+
baseutil.VerifyReport(t, test.in, r)
1798+
assert.Equal(t, test.out, actual, "translation mismatch")
1799+
assert.Equal(t, test.report, r, "report mismatch")
1800+
baseutil.VerifyTranslations(t, translations, test.exceptions)
1801+
assert.NoError(t, translations.DebugVerifyCoverage(actual), "incomplete TranslationSet coverage")
1802+
})
1803+
}
1804+
1805+
// TODO: testing with two modules and merge those two tests and other scenerios
1806+
func TestTranslateModule(t *testing.T) {
1807+
translations := []translate.Translation{
1808+
{From: path.New("yaml", "version"), To: path.New("json", "ignition", "version")},
1809+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage")},
1810+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "filesystems")},
1811+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "filesystems", 0)},
1812+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "filesystems", 0, "path")},
1813+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "filesystems", 0, "device")},
1814+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "filesystems", 0, "format")},
1815+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "files")},
1816+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "files", 0)},
1817+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "files", 0, "path")},
1818+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "files", 0, "append")},
1819+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "files", 0, "append", 0)},
1820+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "files", 0, "append", 0, "source")},
1821+
{From: path.New("yaml", "selinux", "module"), To: path.New("json", "storage", "files", 0, "append", 0, "compression")},
1822+
}
1823+
1824+
// With 2 modules
1825+
in := Config{
1826+
Selinux: Selinux{
1827+
Module: []Module{
1828+
{
1829+
Name: "some_name",
1830+
Content: "some content here",
1831+
},
1832+
{
1833+
Name: "another_name",
1834+
Content: "some content here",
1835+
},
1836+
},
1837+
},
1838+
}
1839+
out := types.Config{
1840+
Ignition: types.Ignition{
1841+
Version: "3.5.0-experimental",
1842+
},
1843+
Storage: types.Storage{
1844+
Filesystems: []types.Filesystem{
1845+
{
1846+
Device: "/dev/disk/by-label/boot",
1847+
Format: util.StrToPtr("ext4"),
1848+
Path: util.StrToPtr("/boot"),
1849+
},
1850+
},
1851+
Files: []types.File{
1852+
{
1853+
Node: types.Node{
1854+
Path: "/etc/selinux/targeted/modules/active/extra/some_name.cil",
1855+
},
1856+
FileEmbedded1: types.FileEmbedded1{
1857+
Append: []types.Resource{
1858+
{
1859+
Source: util.StrToPtr("data:,some%20content%20here"),
1860+
Compression: util.StrToPtr(""),
1861+
},
1862+
},
1863+
},
1864+
},
1865+
{
1866+
Node: types.Node{
1867+
Path: "/etc/selinux/targeted/modules/active/extra/another_name.cil",
1868+
},
1869+
FileEmbedded1: types.FileEmbedded1{
1870+
Append: []types.Resource{
1871+
{
1872+
Source: util.StrToPtr("data:,some%20content%20here"),
1873+
Compression: util.StrToPtr(""),
1874+
},
1875+
},
1876+
},
1877+
},
1878+
},
1879+
},
1880+
}
1881+
1882+
test := struct {
1883+
in Config
1884+
out types.Config
1885+
exceptions []translate.Translation
1886+
report report.Report
1887+
}{
1888+
in,
1889+
out,
1890+
translations,
1891+
report.Report{},
1892+
}
1893+
1894+
t.Run(fmt.Sprintf("translate %d", 0), func(t *testing.T) {
1895+
actual, translations, r := test.in.ToIgn3_5Unvalidated(common.TranslateOptions{})
1896+
r = confutil.TranslateReportPaths(r, translations)
1897+
baseutil.VerifyReport(t, test.in, r)
1898+
assert.Equal(t, test.out, actual, "translation mismatch")
1899+
assert.Equal(t, test.report, r, "report mismatch")
1900+
baseutil.VerifyTranslations(t, translations, test.exceptions)
1901+
assert.NoError(t, translations.DebugVerifyCoverage(actual), "incomplete TranslationSet coverage")
1902+
})
1903+
}

0 commit comments

Comments
 (0)