Skip to content

Commit

Permalink
Merge pull request #889 from runcom/fips-all-the-things
Browse files Browse the repository at this point in the history
pkg/daemon: support FIPS
  • Loading branch information
openshift-merge-robot authored Jun 25, 2019
2 parents 62d26bb + eb9186b commit c73d4ad
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 0 deletions.
5 changes: 5 additions & 0 deletions docs/MachineConfiguration.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type MachineConfigSpec struct {
// Config is a Ignition Config object.
Config ign.Config `json:"config"`
KernelArguments []string `json:"kernelArguments"`
Fips bool `json:"fips"`
}
```

Expand Down Expand Up @@ -94,6 +95,10 @@ Ignition config keys as well.

This extends the host's kernel arguments. Use this for e.g. [nosmt](https://access.redhat.com/solutions/rhel-smt).

### FIPS

This allows to enable/disable [FIPS mode](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/security_guide/chap-federal_standards_and_regulations). If any of the configuration has FIPS enabled, it'll be set.

### OSImageURL

You should not attempt to set this field; it is controlled by the operator and injected directly into the final `rendered-` config.
Expand Down
4 changes: 4 additions & 0 deletions lib/resourcemerge/machineconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ func ensureMachineConfigSpec(modified *bool, existing *mcfgv1.MachineConfigSpec,
*modified = true
(*existing).Config = required.Config
}
if existing.Fips != required.Fips {
*modified = true
(*existing).Fips = required.Fips
}
}

func ensureControllerConfigSpec(modified *bool, existing *mcfgv1.ControllerConfigSpec, required mcfgv1.ControllerConfigSpec) {
Expand Down
6 changes: 6 additions & 0 deletions pkg/apis/machineconfiguration.openshift.io/v1/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@ func MergeMachineConfigs(configs []*MachineConfig, osImageURL string) *MachineCo
}
sort.Slice(configs, func(i, j int) bool { return configs[i].Name < configs[j].Name })

var fips bool
outIgn := configs[0].Spec.Config
for idx := 1; idx < len(configs); idx++ {
// if any of the config has FIPS enabled, it'll be set
if configs[idx].Spec.Fips {
fips = true
}
outIgn = ign.Append(outIgn, configs[idx].Spec.Config)
}
kargs := []string{}
Expand All @@ -34,6 +39,7 @@ func MergeMachineConfigs(configs []*MachineConfig, osImageURL string) *MachineCo
OSImageURL: osImageURL,
KernelArguments: kargs,
Config: outIgn,
Fips: fips,
},
}
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/machineconfiguration.openshift.io/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,8 @@ type MachineConfigSpec struct {
Config igntypes.Config `json:"config"`

KernelArguments []string `json:"kernelArguments"`

Fips bool `json:"fips"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
Expand Down
32 changes: 32 additions & 0 deletions pkg/daemon/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ const (
coreUserName = "core"
// SSH Keys for user "core" will only be written at /home/core/.ssh
coreUserSSHPath = "/home/core/.ssh/"
// fipsCommand is the command to use when enabling or disabling FIPS
fipsCommand = "/usr/libexec/rhcos-tools/coreos-fips"
)

func writeFileAtomicallyWithDefaults(fpath string, b []byte) error {
Expand Down Expand Up @@ -245,16 +247,45 @@ func (dn *Daemon) update(oldConfig, newConfig *mcfgv1.MachineConfig) (retErr err
}
}()

if err := dn.updateFIPS(oldConfig, newConfig); err != nil {
return err
}
defer func() {
if retErr != nil {
if err := dn.updateFIPS(newConfig, oldConfig); err != nil {
retErr = errors.Wrapf(retErr, "error rolling back FIPS %v", err)
return
}
}
}()

return dn.updateOSAndReboot(newConfig)
}

func (dn *Daemon) updateFIPS(current, desired *mcfgv1.MachineConfig) error {
if current.Spec.Fips == desired.Spec.Fips {
return nil
}
arg := "enable"
if !desired.Spec.Fips {
arg = "disable"
}
cmd := exec.Command(fipsCommand, arg)
dn.logSystem("Running %s %s", fipsCommand, arg)
if out, err := cmd.CombinedOutput(); err != nil {
return errors.Wrapf(err, "%s FIPS: %s", arg, string(out))
}
return nil
}

// MachineConfigDiff represents an ad-hoc difference between two MachineConfig objects.
// At some point this may change into holding just the files/units that changed
// and the MCO would just operate on that. For now we're just doing this to get
// improved logging.
type MachineConfigDiff struct {
osUpdate bool
kargs bool
fips bool
passwd bool
files bool
units bool
Expand Down Expand Up @@ -369,6 +400,7 @@ func Reconcilable(oldConfig, newConfig *mcfgv1.MachineConfig) (*MachineConfigDif
return &MachineConfigDiff{
osUpdate: oldConfig.Spec.OSImageURL != newConfig.Spec.OSImageURL,
kargs: !reflect.DeepEqual(oldConfig.Spec.KernelArguments, newConfig.Spec.KernelArguments),
fips: oldConfig.Spec.Fips != newConfig.Spec.Fips,
passwd: passwdChanged,
files: !reflect.DeepEqual(oldIgn.Storage.Files, newIgn.Storage.Files),
units: !reflect.DeepEqual(oldIgn.Systemd.Units, newIgn.Systemd.Units),
Expand Down
88 changes: 88 additions & 0 deletions test/e2e/mcd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -546,3 +546,91 @@ func TestDontDeleteRPMFiles(t *testing.T) {
}
}
}

func TestFIPS(t *testing.T) {
cs := framework.NewClientSet("")
bumpPoolMaxUnavailableTo(t, cs, 3)
fipsMC := &mcv1.MachineConfig{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("fips-%s", uuid.NewUUID()),
Labels: mcLabelForWorkers(),
},
Spec: mcv1.MachineConfigSpec{
Config: ctrlcommon.NewIgnConfig(),
Fips: true,
},
}

mcp, err := cs.MachineConfigPools().Get("worker", metav1.GetOptions{})
if err != nil {
t.Error(err)
}
workerOldMc := mcp.Status.Configuration.Name

_, err = cs.MachineConfigs().Create(fipsMC)
if err != nil {
t.Fatal(err)
}
t.Logf("Created %s", fipsMC.Name)
renderedConfig, err := waitForRenderedConfig(t, cs, "worker", fipsMC.Name)
if err != nil {
t.Fatal(err)
}
if err := waitForPoolComplete(t, cs, "worker", renderedConfig); err != nil {
t.Fatal(err)
}
nodes, err := getNodesByRole(cs, "worker")
if err != nil {
t.Fatal(err)
}
for _, node := range nodes {
assert.Equal(t, node.Annotations[constants.CurrentMachineConfigAnnotationKey], renderedConfig)
assert.Equal(t, node.Annotations[constants.MachineConfigDaemonStateAnnotationKey], constants.MachineConfigDaemonStateDone)
mcd, err := mcdForNode(cs, &node)
if err != nil {
t.Fatal(err)
}
mcdName := mcd.ObjectMeta.Name
fipsBytes, err := exec.Command("oc", "rsh", "-n", "openshift-machine-config-operator", mcdName,
"chroot", "/rootfs", "fips-mode-setup", "--check").CombinedOutput()
if err != nil {
t.Fatal(err)
}
fips := string(fipsBytes)
if !strings.Contains(fips, "FIPS mode is enabled") {
t.Fatalf("FIPS hasn't been enabled")
}
t.Logf("Node %s has expected FIPS mode", node.Name)
}

if err := cs.MachineConfigs().Delete(fipsMC.Name, &metav1.DeleteOptions{}); err != nil {
t.Error(err)
}
if err := waitForPoolComplete(t, cs, "worker", workerOldMc); err != nil {
t.Fatal(err)
}

nodes, err = getNodesByRole(cs, "worker")
if err != nil {
t.Fatal(err)
}
for _, node := range nodes {
assert.Equal(t, node.Annotations[constants.CurrentMachineConfigAnnotationKey], workerOldMc)
assert.Equal(t, node.Annotations[constants.MachineConfigDaemonStateAnnotationKey], constants.MachineConfigDaemonStateDone)
mcd, err := mcdForNode(cs, &node)
if err != nil {
t.Fatal(err)
}
mcdName := mcd.ObjectMeta.Name
fipsBytes, err := exec.Command("oc", "rsh", "-n", "openshift-machine-config-operator", mcdName,
"chroot", "/rootfs", "fips-mode-setup", "--check").CombinedOutput()
if err != nil {
t.Fatal(err)
}
fips := string(fipsBytes)
if !strings.Contains(fips, "FIPS mode is disabled") {
t.Fatalf("FIPS hasn't been disabled")
}
t.Logf("Node %s has expected FIPS mode", node.Name)
}
}

0 comments on commit c73d4ad

Please sign in to comment.