Skip to content

Commit

Permalink
Add an automatically generated Ignition provisioning token
Browse files Browse the repository at this point in the history
Part of implementing: openshift/enhancements#443

The installer generates a random token (~password) and injects
it into the Ignition pointer configuration and as a secret into the
cluster.

The MCO will check it.
  • Loading branch information
cgwalters committed Nov 12, 2020
1 parent 9be316b commit a5e1247
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
kind: Secret
apiVersion: v1
metadata:
namespace: openshift-machine-config-operator
name: provisioning-token
data:
token: {{.Base64EncodedKubeadminPwHash}}
2 changes: 2 additions & 0 deletions pkg/asset/cluster/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/openshift/installer/pkg/asset"
"github.com/openshift/installer/pkg/asset/cluster/aws"
"github.com/openshift/installer/pkg/asset/cluster/azure"
"github.com/openshift/installer/pkg/asset/ignition"
"github.com/openshift/installer/pkg/asset/installconfig"
"github.com/openshift/installer/pkg/asset/password"
"github.com/openshift/installer/pkg/asset/quota"
Expand Down Expand Up @@ -51,6 +52,7 @@ func (c *Cluster) Dependencies() []asset.Asset {
&quota.PlatformQuotaCheck{},
&TerraformVariables{},
&password.KubeadminPassword{},
&ignition.ProvisioningToken{},
}
}

Expand Down
6 changes: 4 additions & 2 deletions pkg/asset/ignition/machine/master.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var _ asset.WritableAsset = (*Master)(nil)
func (a *Master) Dependencies() []asset.Asset {
return []asset.Asset{
&installconfig.InstallConfig{},
&ignition.ProvisioningToken{},
&tls.RootCA{},
}
}
Expand All @@ -37,9 +38,10 @@ func (a *Master) Dependencies() []asset.Asset {
func (a *Master) Generate(dependencies asset.Parents) error {
installConfig := &installconfig.InstallConfig{}
rootCA := &tls.RootCA{}
dependencies.Get(installConfig, rootCA)
token := &ignition.ProvisioningToken{}
dependencies.Get(installConfig, rootCA, token)

a.Config = pointerIgnitionConfig(installConfig.Config, rootCA.Cert(), "master")
a.Config = pointerIgnitionConfig(installConfig.Config, rootCA.Cert(), token.Token, "master")

data, err := ignition.Marshal(a.Config)
if err != nil {
Expand Down
6 changes: 5 additions & 1 deletion pkg/asset/ignition/machine/master_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"k8s.io/utils/pointer"

"github.com/openshift/installer/pkg/asset"
"github.com/openshift/installer/pkg/asset/ignition"
"github.com/openshift/installer/pkg/asset/installconfig"
"github.com/openshift/installer/pkg/asset/tls"
"github.com/openshift/installer/pkg/ipnet"
Expand Down Expand Up @@ -41,9 +42,12 @@ func TestMasterGenerate(t *testing.T) {
rootCA := &tls.RootCA{}
err := rootCA.Generate(nil)
assert.NoError(t, err, "unexpected error generating root CA")
token := &ignition.ProvisioningToken{}
err = token.Generate(nil)
assert.NoError(t, err, "unexpected error generating token")

parents := asset.Parents{}
parents.Add(installConfig, rootCA)
parents.Add(installConfig, rootCA, token)

master := &Master{}
err = master.Generate(parents)
Expand Down
11 changes: 7 additions & 4 deletions pkg/asset/ignition/machine/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (

// pointerIgnitionConfig generates a config which references the remote config
// served by the machine config server.
func pointerIgnitionConfig(installConfig *types.InstallConfig, rootCA []byte, role string) *igntypes.Config {
func pointerIgnitionConfig(installConfig *types.InstallConfig, rootCA []byte, token, role string) *igntypes.Config {
var ignitionHost string
// Default platform independent ignitionHost
ignitionHost = fmt.Sprintf("api-int.%s:22623", installConfig.ClusterDomain())
Expand All @@ -37,16 +37,19 @@ func pointerIgnitionConfig(installConfig *types.InstallConfig, rootCA []byte, ro
ignitionHost = net.JoinHostPort(installConfig.VSphere.APIVIP, "22623")
}
}
queryvals := url.Values{}
queryvals.Add("token", token)
return &igntypes.Config{
Ignition: igntypes.Ignition{
Version: igntypes.MaxVersion.String(),
Config: igntypes.IgnitionConfig{
Merge: []igntypes.Resource{{
Source: ignutil.StrToPtr(func() *url.URL {
return &url.URL{
Scheme: "https",
Host: ignitionHost,
Path: fmt.Sprintf("/config/%s", role),
Scheme: "https",
Host: ignitionHost,
Path: fmt.Sprintf("/config/%s", role),
RawQuery: queryvals.Encode(),
}
}().String()),
}},
Expand Down
6 changes: 4 additions & 2 deletions pkg/asset/ignition/machine/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var _ asset.WritableAsset = (*Worker)(nil)
func (a *Worker) Dependencies() []asset.Asset {
return []asset.Asset{
&installconfig.InstallConfig{},
&ignition.ProvisioningToken{},
&tls.RootCA{},
}
}
Expand All @@ -37,9 +38,10 @@ func (a *Worker) Dependencies() []asset.Asset {
func (a *Worker) Generate(dependencies asset.Parents) error {
installConfig := &installconfig.InstallConfig{}
rootCA := &tls.RootCA{}
dependencies.Get(installConfig, rootCA)
token := &ignition.ProvisioningToken{}
dependencies.Get(installConfig, rootCA, token)

a.Config = pointerIgnitionConfig(installConfig.Config, rootCA.Cert(), "worker")
a.Config = pointerIgnitionConfig(installConfig.Config, rootCA.Cert(), token.Token, "worker")

data, err := ignition.Marshal(a.Config)
if err != nil {
Expand Down
6 changes: 5 additions & 1 deletion pkg/asset/ignition/machine/worker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/stretchr/testify/assert"

"github.com/openshift/installer/pkg/asset"
"github.com/openshift/installer/pkg/asset/ignition"
"github.com/openshift/installer/pkg/asset/installconfig"
"github.com/openshift/installer/pkg/asset/tls"
"github.com/openshift/installer/pkg/ipnet"
Expand All @@ -31,9 +32,12 @@ func TestWorkerGenerate(t *testing.T) {
rootCA := &tls.RootCA{}
err := rootCA.Generate(nil)
assert.NoError(t, err, "unexpected error generating root CA")
token := &ignition.ProvisioningToken{}
err = token.Generate(nil)
assert.NoError(t, err, "unexpected error generating token")

parents := asset.Parents{}
parents.Add(installConfig, rootCA)
parents.Add(installConfig, rootCA, token)

worker := &Worker{}
err = worker.Generate(parents)
Expand Down
46 changes: 46 additions & 0 deletions pkg/asset/ignition/provisioningtoken.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package ignition

import (
"crypto/rand"
"encoding/base64"
"fmt"

"github.com/openshift/installer/pkg/asset"
)

// tokenLen is how many bytes of random input go into generating the token.
// It should be a multiple of 3 to render nicely in base64
// encoding. The current default is long; it's a lot of entropy. The MCS
// will have mitigations against brute forcing.
const tokenLen = 30

// ProvisioningToken implements https://github.com/openshift/enhancements/pull/443/
type ProvisioningToken struct {
Token string
}

var _ asset.Asset = (*ProvisioningToken)(nil)

// Dependencies returns no dependencies.
func (a *ProvisioningToken) Dependencies() []asset.Asset {
return []asset.Asset{}
}

// Generate the token
func (a *ProvisioningToken) Generate(asset.Parents) error {
b := make([]byte, tokenLen)
n, err := rand.Read(b)
if err != nil {
return err
}
if n != tokenLen {
panic(fmt.Sprintf("Expected %d bytes", tokenLen))
}
a.Token = base64.StdEncoding.EncodeToString(b)
return nil
}

// Name returns the human-friendly name of the asset.
func (a *ProvisioningToken) Name() string {
return "Ignition Provisioning Password"
}
12 changes: 10 additions & 2 deletions pkg/asset/manifests/openshift.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/pkg/errors"

"github.com/openshift/installer/pkg/asset"
"github.com/openshift/installer/pkg/asset/ignition"
"github.com/openshift/installer/pkg/asset/installconfig"
"github.com/openshift/installer/pkg/asset/installconfig/gcp"
"github.com/openshift/installer/pkg/asset/installconfig/ovirt"
Expand Down Expand Up @@ -57,10 +58,12 @@ func (o *Openshift) Dependencies() []asset.Asset {
&installconfig.InstallConfig{},
&installconfig.ClusterID{},
&password.KubeadminPassword{},
&ignition.ProvisioningToken{},
&openshiftinstall.Config{},

&openshift.CloudCredsSecret{},
&openshift.KubeadminPasswordSecret{},
&openshift.IgnitionProvisioningSecret{},
&openshift.RoleCloudCredsSecretReader{},
&openshift.PrivateClusterOutbound{},
&openshift.BaremetalConfig{},
Expand All @@ -73,8 +76,9 @@ func (o *Openshift) Generate(dependencies asset.Parents) error {
installConfig := &installconfig.InstallConfig{}
clusterID := &installconfig.ClusterID{}
kubeadminPassword := &password.KubeadminPassword{}
ignitionProvisioningToken := &ignition.ProvisioningToken{}
openshiftInstall := &openshiftinstall.Config{}
dependencies.Get(installConfig, kubeadminPassword, clusterID, openshiftInstall)
dependencies.Get(installConfig, kubeadminPassword, clusterID, openshiftInstall, ignitionProvisioningToken)
var cloudCreds cloudCredsSecretData
platform := installConfig.Config.Platform.Name()
switch platform {
Expand Down Expand Up @@ -186,24 +190,28 @@ func (o *Openshift) Generate(dependencies asset.Parents) error {

templateData := &openshiftTemplateData{
CloudCreds: cloudCreds,
IgnitionProvisioningToken: ignitionProvisioningToken.Token,
Base64EncodedKubeadminPwHash: base64.StdEncoding.EncodeToString(kubeadminPassword.PasswordHash),
}

cloudCredsSecret := &openshift.CloudCredsSecret{}
kubeadminPasswordSecret := &openshift.KubeadminPasswordSecret{}
ignitionProvisioningSecret := &openshift.IgnitionProvisioningSecret{}
roleCloudCredsSecretReader := &openshift.RoleCloudCredsSecretReader{}
baremetalConfig := &openshift.BaremetalConfig{}
rhcosImage := new(rhcos.Image)

dependencies.Get(
cloudCredsSecret,
kubeadminPasswordSecret,
ignitionProvisioningSecret,
roleCloudCredsSecretReader,
baremetalConfig,
rhcosImage)

assetData := map[string][]byte{
"99_kubeadmin-password-secret.yaml": applyTemplateData(kubeadminPasswordSecret.Files()[0].Data, templateData),
"99_ignition-provisioning-secret.yaml": applyTemplateData(ignitionProvisioningSecret.Files()[0].Data, templateData),
"99_kubeadmin-password-secret.yaml": applyTemplateData(kubeadminPasswordSecret.Files()[0].Data, templateData),
}

switch platform {
Expand Down
1 change: 1 addition & 0 deletions pkg/asset/manifests/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,6 @@ type baremetalTemplateData struct {

type openshiftTemplateData struct {
CloudCreds cloudCredsSecretData
IgnitionProvisioningToken string
Base64EncodedKubeadminPwHash string
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package openshift

import (
"os"
"path/filepath"

"github.com/openshift/installer/pkg/asset"
"github.com/openshift/installer/pkg/asset/templates/content"
)

const (
fileName = "ignition-provisioning-secret.yaml.template"
)

var _ asset.WritableAsset = (*IgnitionProvisioningSecret)(nil)

// IgnitionProvisioningSecret implements https://github.com/openshift/enhancements/pull/443
type IgnitionProvisioningSecret struct {
FileList []*asset.File
}

// Dependencies returns all of the dependencies directly needed by the asset
func (t *IgnitionProvisioningSecret) Dependencies() []asset.Asset {
return []asset.Asset{}
}

// Name returns the human-friendly name of the asset.
func (t *IgnitionProvisioningSecret) Name() string {
return "IgnitionProvisioningSecret"
}

// Generate generates the actual files by this asset
func (t *IgnitionProvisioningSecret) Generate(parents asset.Parents) error {
data, err := content.GetOpenshiftTemplate(fileName)
if err != nil {
return err
}
t.FileList = []*asset.File{
{
Filename: filepath.Join(content.TemplateDir, fileName),
Data: []byte(data),
},
}
return nil
}

// Files returns the files generated by the asset.
func (t *IgnitionProvisioningSecret) Files() []*asset.File {
return t.FileList
}

// Load returns the asset from disk.
func (t *IgnitionProvisioningSecret) Load(f asset.FileFetcher) (bool, error) {
file, err := f.FetchByName(filepath.Join(content.TemplateDir, fileName))
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
t.FileList = []*asset.File{file}
return true, nil
}

0 comments on commit a5e1247

Please sign in to comment.