mirror of
https://github.com/rancher/os.git
synced 2025-09-06 09:14:31 +00:00
Add TPM and MachineRegister support
This commit is contained in:
@@ -1,9 +1,9 @@
|
|||||||
FROM opensuse/leap:15.3 AS build
|
FROM opensuse/leap:15.3 AS build
|
||||||
RUN zypper ref
|
RUN zypper ref
|
||||||
RUN zypper in -y squashfs xorriso go1.16 upx busybox-static curl tar git gzip
|
RUN zypper in -y squashfs xorriso go1.16 upx busybox-static curl tar git gzip
|
||||||
RUN curl -Lo /usr/bin/luet https://github.com/mudler/luet/releases/download/0.18.1/luet-0.18.1-linux-$(go env GOARCH) && \
|
RUN curl -Lo /usr/bin/luet https://github.com/mudler/luet/releases/download/0.20.5/luet-0.20.5-linux-$(go env GOARCH) && \
|
||||||
chmod +x /usr/bin/luet
|
chmod +x /usr/bin/luet
|
||||||
RUN curl -Lo /usr/bin/rancherd https://github.com/rancher/rancherd/releases/download/v0.0.1-alpha10/rancherd-$(go env GOARCH) && \
|
RUN curl -Lo /usr/bin/rancherd https://github.com/rancher/rancherd/releases/download/v0.0.1-alpha11/rancherd-$(go env GOARCH) && \
|
||||||
chmod +x /usr/bin/rancherd
|
chmod +x /usr/bin/rancherd
|
||||||
RUN curl -L https://get.helm.sh/helm-v3.7.1-linux-$(go env GOARCH).tar.gz | tar xzf - -C /usr/bin --strip-components=1
|
RUN curl -L https://get.helm.sh/helm-v3.7.1-linux-$(go env GOARCH).tar.gz | tar xzf - -C /usr/bin --strip-components=1
|
||||||
COPY go.mod go.sum /usr/src/
|
COPY go.mod go.sum /usr/src/
|
||||||
@@ -90,6 +90,7 @@ RUN zypper in -y \
|
|||||||
kernel-firmware-qlogic \
|
kernel-firmware-qlogic \
|
||||||
kernel-firmware-realtek \
|
kernel-firmware-realtek \
|
||||||
kernel-firmware-usb-network \
|
kernel-firmware-usb-network \
|
||||||
|
libtspi1 \
|
||||||
less \
|
less \
|
||||||
lshw \
|
lshw \
|
||||||
lsof \
|
lsof \
|
||||||
|
@@ -4,7 +4,7 @@ RUN zypper ref
|
|||||||
ARG DAPPER_HOST_ARCH
|
ARG DAPPER_HOST_ARCH
|
||||||
ENV ARCH $DAPPER_HOST_ARCH
|
ENV ARCH $DAPPER_HOST_ARCH
|
||||||
|
|
||||||
RUN zypper in -y bash git gcc docker vim less file curl wget ca-certificates make mkisofs go1.16 qemu-tools
|
RUN zypper in -y bash git gcc docker vim less file curl wget ca-certificates make mkisofs go1.16 qemu-tools trousers-devel
|
||||||
RUN go get golang.org/x/tools/cmd/goimports
|
RUN go get golang.org/x/tools/cmd/goimports
|
||||||
RUN if [ "${ARCH}" == "amd64" ]; then \
|
RUN if [ "${ARCH}" == "amd64" ]; then \
|
||||||
curl -sL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s v1.40.1; \
|
curl -sL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s v1.40.1; \
|
||||||
|
30
Dockerfile.kvm
Normal file
30
Dockerfile.kvm
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
FROM opensuse/leap:15.3
|
||||||
|
RUN zypper ref
|
||||||
|
RUN zypper install -y socat net-tools-deprecated libtasn1-devel gnutls-devel libseccomp-devel json-glib-devel system-user-tss git
|
||||||
|
RUN zypper install -y autoconf
|
||||||
|
RUN zypper install -y automake
|
||||||
|
RUN git clone https://github.com/stefanberger/swtpm.git /usr/src/swtpm
|
||||||
|
RUN zypper install -y libtool
|
||||||
|
RUN zypper install -y gcc
|
||||||
|
RUN zypper install -y libopenssl-devel
|
||||||
|
RUN git clone https://github.com/stefanberger/libtpms.git /usr/src/libtpms
|
||||||
|
RUN zypper install -y gcc-c++
|
||||||
|
RUN zypper install -y make
|
||||||
|
RUN zypper install -y expect
|
||||||
|
RUN zypper install -y sudo
|
||||||
|
RUN cd /usr/src/libtpms && \
|
||||||
|
./autogen.sh --with-openssl --with-tpm2 && \
|
||||||
|
make -j4 && \
|
||||||
|
make install
|
||||||
|
RUN cd /usr/src/swtpm && \
|
||||||
|
./autogen.sh --prefix=/usr --libdir=/usr/lib64 --with-openssl --with-tss-user=root --with-tss-group=tss && \
|
||||||
|
make -j4 && \
|
||||||
|
sudo make -j4 && \
|
||||||
|
sudo make install
|
||||||
|
RUN zypper install -y qemu-x86 qemu-arm qemu-tools
|
||||||
|
|
||||||
|
COPY scripts/qemu-in-container /usr/bin/
|
||||||
|
ENTRYPOINT ["/usr/bin/qemu-in-container"]
|
||||||
|
|
||||||
|
RUN chmod +s /usr/lib/qemu-bridge-helper
|
||||||
|
RUN echo 'allow all' > /etc/qemu/bridge.conf
|
@@ -34,9 +34,14 @@ rules:
|
|||||||
- apiGroups:
|
- apiGroups:
|
||||||
- management.cattle.io
|
- management.cattle.io
|
||||||
resources:
|
resources:
|
||||||
- 'settings'
|
|
||||||
- 'clusterregistrationtokens'
|
- 'clusterregistrationtokens'
|
||||||
verbs:
|
verbs:
|
||||||
|
- '*'
|
||||||
|
- apiGroups:
|
||||||
|
- management.cattle.io
|
||||||
|
resources:
|
||||||
|
- 'settings'
|
||||||
|
verbs:
|
||||||
- 'get'
|
- 'get'
|
||||||
- 'watch'
|
- 'watch'
|
||||||
- 'list'
|
- 'list'
|
||||||
@@ -44,8 +49,6 @@ rules:
|
|||||||
- apiextensions.k8s.io
|
- apiextensions.k8s.io
|
||||||
resources:
|
resources:
|
||||||
- customresourcedefinitions
|
- customresourcedefinitions
|
||||||
#resourceNames:
|
|
||||||
#- managedosimages.rancheros.cattle.io
|
|
||||||
verbs:
|
verbs:
|
||||||
- '*'
|
- '*'
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
|
@@ -14,6 +14,8 @@ var (
|
|||||||
automatic = flag.Bool("automatic", false, "Check for and run automatic installation")
|
automatic = flag.Bool("automatic", false, "Check for and run automatic installation")
|
||||||
printConfig = flag.Bool("print-config", false, "Print effective configuration and exit")
|
printConfig = flag.Bool("print-config", false, "Print effective configuration and exit")
|
||||||
configFile = flag.String("config-file", "", "Config file to use, local file or http/tftp URL")
|
configFile = flag.String("config-file", "", "Config file to use, local file or http/tftp URL")
|
||||||
|
powerOff = flag.Bool("power-off", false, "Power off after installation")
|
||||||
|
yes = flag.Bool("y", false, "Do not prompt for questions")
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -31,7 +33,7 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := install.Run(*automatic, *configFile); err != nil {
|
if err := install.Run(*automatic, *configFile, *powerOff, *yes); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,3 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
ConditionPathExists=!/run/cos/live_mode
|
ConditionPathExists=!/run/cos/live_mode
|
||||||
ConditionPathExists=!/run/cos/rescue_mode
|
ConditionPathExists=!/run/cos/recovery_mode
|
||||||
|
@@ -18,5 +18,6 @@ LimitNOFILE=1048576
|
|||||||
LimitNPROC=infinity
|
LimitNPROC=infinity
|
||||||
LimitCORE=infinity
|
LimitCORE=infinity
|
||||||
TasksMax=infinity
|
TasksMax=infinity
|
||||||
|
StandardOutput=journal+console
|
||||||
TimeoutStartSec=0
|
TimeoutStartSec=0
|
||||||
ExecStart=/usr/bin/rancherd bootstrap
|
ExecStart=/usr/bin/rancherd bootstrap
|
||||||
|
@@ -15,6 +15,14 @@ spec:
|
|||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if [ -e /etc/rancher/rke2/rke2.yaml ]; then
|
||||||
|
export KUBECONFIG=/etc/rancher/rke2/rke2.yaml
|
||||||
|
elif [ -e /etc/rancher/k3s/k3s.yaml ]; then
|
||||||
|
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
|
||||||
|
else
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
PULL_POLICY=IfNotPresent
|
PULL_POLICY=IfNotPresent
|
||||||
if [ "$IMAGE_TAG" = dev ]; then
|
if [ "$IMAGE_TAG" = dev ]; then
|
||||||
PULL_POLICY=Always
|
PULL_POLICY=Always
|
||||||
@@ -29,6 +37,11 @@ helm upgrade \
|
|||||||
--set image.imagePullPolicy=${PULL_POLICY} \
|
--set image.imagePullPolicy=${PULL_POLICY} \
|
||||||
rancheros-operator /usr/share/rancher/os2/rancheros-operator-chart.tgz
|
rancheros-operator /usr/share/rancher/os2/rancheros-operator-chart.tgz
|
||||||
|
|
||||||
|
while ! kubectl get crd managedosimages.rancheros.cattle.io; do
|
||||||
|
echo Waiting for RancherOS Operator to be running
|
||||||
|
sleep 15
|
||||||
|
done
|
||||||
|
|
||||||
while ! manifest | kubectl apply -f -; do
|
while ! manifest | kubectl apply -f -; do
|
||||||
sleep 15
|
sleep 15
|
||||||
done
|
done
|
||||||
|
39
go.mod
39
go.mod
@@ -2,25 +2,58 @@ module github.com/rancher/os2
|
|||||||
|
|
||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
|
replace (
|
||||||
|
k8s.io/api => k8s.io/api v0.22.2
|
||||||
|
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.22.2
|
||||||
|
k8s.io/apimachinery => k8s.io/apimachinery v0.22.2
|
||||||
|
k8s.io/apiserver => k8s.io/apiserver v0.22.2
|
||||||
|
k8s.io/cli-runtime => k8s.io/cli-runtime v0.22.2
|
||||||
|
k8s.io/client-go => k8s.io/client-go v0.22.2
|
||||||
|
k8s.io/cloud-provider => k8s.io/cloud-provider v0.22.2
|
||||||
|
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.22.2
|
||||||
|
k8s.io/code-generator => k8s.io/code-generator v0.22.2
|
||||||
|
k8s.io/component-base => k8s.io/component-base v0.22.2
|
||||||
|
k8s.io/component-helpers => k8s.io/component-helpers v0.22.2
|
||||||
|
k8s.io/controller-manager => k8s.io/controller-manager v0.22.2
|
||||||
|
k8s.io/cri-api => k8s.io/cri-api v0.22.2
|
||||||
|
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.22.2
|
||||||
|
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.22.2
|
||||||
|
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.22.2
|
||||||
|
k8s.io/kube-proxy => k8s.io/kube-proxy v0.22.2
|
||||||
|
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.22.2
|
||||||
|
k8s.io/kubectl => k8s.io/kubectl v0.22.2
|
||||||
|
k8s.io/kubelet => k8s.io/kubelet v0.22.2
|
||||||
|
k8s.io/kubernetes => k8s.io/kubernetes v1.22.2
|
||||||
|
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.22.2
|
||||||
|
k8s.io/metrics => k8s.io/metrics v0.22.2
|
||||||
|
k8s.io/mount-utils => k8s.io/mount-utils v0.22.2
|
||||||
|
k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.22.2
|
||||||
|
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.22.2
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/google/certificate-transparency-go v1.1.2
|
||||||
|
github.com/google/go-attestation v0.3.2
|
||||||
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/mattn/go-isatty v0.0.12
|
github.com/mattn/go-isatty v0.0.12
|
||||||
github.com/pin/tftp v2.1.0+incompatible // indirect
|
github.com/pin/tftp v2.1.0+incompatible // indirect
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/rancher/fleet/pkg/apis v0.0.0-20210927195558-4aaa778d23dd
|
github.com/rancher/fleet/pkg/apis v0.0.0-20210927195558-4aaa778d23dd
|
||||||
github.com/rancher/lasso v0.0.0-20210709145333-6c6cd7fd6607
|
github.com/rancher/lasso v0.0.0-20210709145333-6c6cd7fd6607
|
||||||
github.com/rancher/rancher/pkg/apis v0.0.0-20211013185633-a636bda2a00e
|
github.com/rancher/rancher/pkg/apis v0.0.0-20211013185633-a636bda2a00e
|
||||||
|
github.com/rancher/rancherd v0.0.1-alpha9.0.20211028172625-bdf5642d62d5
|
||||||
github.com/rancher/steve v0.0.0-20210922195510-7224dc21013d
|
github.com/rancher/steve v0.0.0-20210922195510-7224dc21013d
|
||||||
github.com/rancher/system-upgrade-controller/pkg/apis v0.0.0-20210929162341-5e6e996d9486
|
github.com/rancher/system-upgrade-controller/pkg/apis v0.0.0-20210929162341-5e6e996d9486
|
||||||
github.com/rancher/wrangler v0.8.7
|
github.com/rancher/wrangler v0.8.7
|
||||||
github.com/sirupsen/logrus v1.7.0
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/tredoe/osutil v1.0.5
|
github.com/tredoe/osutil v1.0.5
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
|
||||||
gopkg.in/pin/tftp.v2 v2.1.0
|
gopkg.in/pin/tftp.v2 v2.1.0
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||||
|
gotest.tools v2.2.0+incompatible
|
||||||
k8s.io/api v0.22.2
|
k8s.io/api v0.22.2
|
||||||
k8s.io/apimachinery v0.22.2
|
k8s.io/apimachinery v0.22.2
|
||||||
k8s.io/client-go v12.0.0+incompatible
|
k8s.io/client-go v12.0.0+incompatible
|
||||||
sigs.k8s.io/controller-runtime v0.9.0-beta.0
|
sigs.k8s.io/controller-runtime v0.9.0-beta.0
|
||||||
sigs.k8s.io/yaml v1.2.0
|
sigs.k8s.io/yaml v1.2.0
|
||||||
)
|
)
|
||||||
|
|
||||||
replace k8s.io/client-go => k8s.io/client-go v0.22.2
|
|
||||||
|
@@ -48,6 +48,7 @@ nav:
|
|||||||
- installation.md
|
- installation.md
|
||||||
- upgrade.md
|
- upgrade.md
|
||||||
- configuration.md
|
- configuration.md
|
||||||
|
- customizing.md
|
||||||
- dashboard.md
|
- dashboard.md
|
||||||
- operator.md
|
- operator.md
|
||||||
- versions.md
|
- versions.md
|
||||||
|
@@ -1,12 +1,7 @@
|
|||||||
if [ -z "$KUBECONFIG" ]; then
|
if [ -z "$KUBECONFIG" ]; then
|
||||||
if [ -e /etc/rancher/rke2 ]; then
|
export KUBECONFIG="/etc/rancher/k3s/k3s.yaml:/etc/rancher/rke2/rke2.yaml"
|
||||||
export KUBECONFIG=/etc/rancher/rke2/rke2.yaml
|
|
||||||
fi
|
|
||||||
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
|
|
||||||
fi
|
|
||||||
if [ -d /var/lib/rancher/rke2/bin ]; then
|
|
||||||
export PATH="${PATH}:/var/lib/rancher/rke2/bin"
|
|
||||||
fi
|
fi
|
||||||
|
export PATH="${PATH}:/var/lib/rancher/rke2/bin"
|
||||||
if [ -z "$CONTAINER_RUNTIME_ENDPOINT" ]; then
|
if [ -z "$CONTAINER_RUNTIME_ENDPOINT" ]; then
|
||||||
export CONTAINER_RUNTIME_ENDPOINT=unix:///var/run/k3s/containerd/containerd.sock
|
export CONTAINER_RUNTIME_ENDPOINT=unix:///var/run/k3s/containerd/containerd.sock
|
||||||
fi
|
fi
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
package v1
|
package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/rancher/fleet/pkg/apis/fleet.cattle.io/v1alpha1"
|
||||||
|
"github.com/rancher/wrangler/pkg/genericcondition"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
@@ -8,6 +10,30 @@ import (
|
|||||||
// +genclient
|
// +genclient
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
|
type MachineRegistration struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
Spec MachineRegistrationSpec `json:"spec"`
|
||||||
|
Status MachineRegistrationStatus `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MachineRegistrationSpec struct {
|
||||||
|
MachineName string `json:"machineName,omitempty"`
|
||||||
|
MachineInventoryLabels map[string]string `json:"machineInventoryLabels,omitempty"`
|
||||||
|
MachineInventoryAnnotations map[string]string `json:"machineInventoryAnnotations,omitempty"`
|
||||||
|
CloudConfig *v1alpha1.GenericMap `json:"cloudConfig,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MachineRegistrationStatus struct {
|
||||||
|
Conditions []genericcondition.GenericCondition `json:"conditions,omitempty"`
|
||||||
|
RegistrationURL string `json:"registrationURL,omitempty"`
|
||||||
|
RegistrationToken string `json:"registrationToken,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// +genclient
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
type MachineInventory struct {
|
type MachineInventory struct {
|
||||||
metav1.TypeMeta `json:",inline"`
|
metav1.TypeMeta `json:",inline"`
|
||||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
@@ -17,19 +43,20 @@ type MachineInventory struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MachineInventorySpec struct {
|
type MachineInventorySpec struct {
|
||||||
ClusterName string `json:"clusterName,omitempty"`
|
TPMHash string `json:"tpmHash,omitempty"`
|
||||||
|
SMBIOS *v1alpha1.GenericMap `json:"smbios,omitempty"`
|
||||||
|
ClusterName string `json:"clusterName"`
|
||||||
MachineTokenSecretName string `json:"machineTokenSecretName,omitempty"`
|
MachineTokenSecretName string `json:"machineTokenSecretName,omitempty"`
|
||||||
Config MachineRuntimeConfig `json:"config,omitempty"`
|
Config MachineRuntimeConfig `json:"config,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MachineRuntimeConfig struct {
|
type MachineRuntimeConfig struct {
|
||||||
Role string `json:"role,omitempty"`
|
Role string `json:"role"`
|
||||||
NodeName string `json:"nodeName,omitempty"`
|
NodeName string `json:"nodeName,omitempty"`
|
||||||
Address string `json:"address,omitempty"`
|
Address string `json:"address,omitempty"`
|
||||||
InternalAddress string `json:"internalAddress,omitempty"`
|
InternalAddress string `json:"internalAddress,omitempty"`
|
||||||
Taints []corev1.Taint `json:"taints,omitempty"`
|
Taints []corev1.Taint `json:"taints,omitempty"`
|
||||||
Labels map[string]string `json:"labels,omitempty"`
|
Labels map[string]string `json:"labels"`
|
||||||
ConfigValues map[string]string `json:"extraConfig,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type MachineInventoryStatus struct {
|
type MachineInventoryStatus struct {
|
||||||
|
@@ -23,6 +23,7 @@ package v1
|
|||||||
import (
|
import (
|
||||||
v1alpha1 "github.com/rancher/fleet/pkg/apis/fleet.cattle.io/v1alpha1"
|
v1alpha1 "github.com/rancher/fleet/pkg/apis/fleet.cattle.io/v1alpha1"
|
||||||
upgradecattleiov1 "github.com/rancher/system-upgrade-controller/pkg/apis/upgrade.cattle.io/v1"
|
upgradecattleiov1 "github.com/rancher/system-upgrade-controller/pkg/apis/upgrade.cattle.io/v1"
|
||||||
|
genericcondition "github.com/rancher/wrangler/pkg/genericcondition"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
@@ -92,6 +93,10 @@ func (in *MachineInventoryList) DeepCopyObject() runtime.Object {
|
|||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *MachineInventorySpec) DeepCopyInto(out *MachineInventorySpec) {
|
func (in *MachineInventorySpec) DeepCopyInto(out *MachineInventorySpec) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
if in.SMBIOS != nil {
|
||||||
|
in, out := &in.SMBIOS, &out.SMBIOS
|
||||||
|
*out = (*in).DeepCopy()
|
||||||
|
}
|
||||||
in.Config.DeepCopyInto(&out.Config)
|
in.Config.DeepCopyInto(&out.Config)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -122,6 +127,122 @@ func (in *MachineInventoryStatus) DeepCopy() *MachineInventoryStatus {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *MachineRegistration) DeepCopyInto(out *MachineRegistration) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
|
in.Spec.DeepCopyInto(&out.Spec)
|
||||||
|
in.Status.DeepCopyInto(&out.Status)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineRegistration.
|
||||||
|
func (in *MachineRegistration) DeepCopy() *MachineRegistration {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(MachineRegistration)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *MachineRegistration) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *MachineRegistrationList) DeepCopyInto(out *MachineRegistrationList) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||||
|
if in.Items != nil {
|
||||||
|
in, out := &in.Items, &out.Items
|
||||||
|
*out = make([]MachineRegistration, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineRegistrationList.
|
||||||
|
func (in *MachineRegistrationList) DeepCopy() *MachineRegistrationList {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(MachineRegistrationList)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *MachineRegistrationList) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *MachineRegistrationSpec) DeepCopyInto(out *MachineRegistrationSpec) {
|
||||||
|
*out = *in
|
||||||
|
if in.MachineInventoryLabels != nil {
|
||||||
|
in, out := &in.MachineInventoryLabels, &out.MachineInventoryLabels
|
||||||
|
*out = make(map[string]string, len(*in))
|
||||||
|
for key, val := range *in {
|
||||||
|
(*out)[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.MachineInventoryAnnotations != nil {
|
||||||
|
in, out := &in.MachineInventoryAnnotations, &out.MachineInventoryAnnotations
|
||||||
|
*out = make(map[string]string, len(*in))
|
||||||
|
for key, val := range *in {
|
||||||
|
(*out)[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.CloudConfig != nil {
|
||||||
|
in, out := &in.CloudConfig, &out.CloudConfig
|
||||||
|
*out = (*in).DeepCopy()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineRegistrationSpec.
|
||||||
|
func (in *MachineRegistrationSpec) DeepCopy() *MachineRegistrationSpec {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(MachineRegistrationSpec)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *MachineRegistrationStatus) DeepCopyInto(out *MachineRegistrationStatus) {
|
||||||
|
*out = *in
|
||||||
|
if in.Conditions != nil {
|
||||||
|
in, out := &in.Conditions, &out.Conditions
|
||||||
|
*out = make([]genericcondition.GenericCondition, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineRegistrationStatus.
|
||||||
|
func (in *MachineRegistrationStatus) DeepCopy() *MachineRegistrationStatus {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(MachineRegistrationStatus)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *MachineRuntimeConfig) DeepCopyInto(out *MachineRuntimeConfig) {
|
func (in *MachineRuntimeConfig) DeepCopyInto(out *MachineRuntimeConfig) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@@ -139,13 +260,6 @@ func (in *MachineRuntimeConfig) DeepCopyInto(out *MachineRuntimeConfig) {
|
|||||||
(*out)[key] = val
|
(*out)[key] = val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if in.ConfigValues != nil {
|
|
||||||
in, out := &in.ConfigValues, &out.ConfigValues
|
|
||||||
*out = make(map[string]string, len(*in))
|
|
||||||
for key, val := range *in {
|
|
||||||
(*out)[key] = val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -43,6 +43,23 @@ func NewMachineInventory(namespace, name string, obj MachineInventory) *MachineI
|
|||||||
|
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
|
// MachineRegistrationList is a list of MachineRegistration resources
|
||||||
|
type MachineRegistrationList struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ListMeta `json:"metadata"`
|
||||||
|
|
||||||
|
Items []MachineRegistration `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMachineRegistration(namespace, name string, obj MachineRegistration) *MachineRegistration {
|
||||||
|
obj.APIVersion, obj.Kind = SchemeGroupVersion.WithKind("MachineRegistration").ToAPIVersionAndKind()
|
||||||
|
obj.Name = name
|
||||||
|
obj.Namespace = namespace
|
||||||
|
return &obj
|
||||||
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
// ManagedOSImageList is a list of ManagedOSImage resources
|
// ManagedOSImageList is a list of ManagedOSImage resources
|
||||||
type ManagedOSImageList struct {
|
type ManagedOSImageList struct {
|
||||||
metav1.TypeMeta `json:",inline"`
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
@@ -29,6 +29,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
MachineInventoryResourceName = "machineinventories"
|
MachineInventoryResourceName = "machineinventories"
|
||||||
|
MachineRegistrationResourceName = "machineregistrations"
|
||||||
ManagedOSImageResourceName = "managedosimages"
|
ManagedOSImageResourceName = "managedosimages"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -55,6 +56,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
|||||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||||
&MachineInventory{},
|
&MachineInventory{},
|
||||||
&MachineInventoryList{},
|
&MachineInventoryList{},
|
||||||
|
&MachineRegistration{},
|
||||||
|
&MachineRegistrationList{},
|
||||||
&ManagedOSImage{},
|
&ManagedOSImage{},
|
||||||
&ManagedOSImageList{},
|
&ManagedOSImageList{},
|
||||||
)
|
)
|
||||||
|
@@ -18,11 +18,14 @@ type Install struct {
|
|||||||
Token string `json:"-"`
|
Token string `json:"-"`
|
||||||
Role string `json:"-"`
|
Role string `json:"-"`
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
|
RegistrationURL string `json:"registrationUrl,omitempty"`
|
||||||
|
RegistrationCACert string `json:"registrationCaCert,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
SSHAuthorizedKeys []string `json:"sshAuthorizedKeys,omitempty"`
|
SSHAuthorizedKeys []string `json:"sshAuthorizedKeys,omitempty"`
|
||||||
RancherOS RancherOS `json:"rancheros,omitempty"`
|
RancherOS RancherOS `json:"rancheros,omitempty"`
|
||||||
|
Data map[string]interface{} `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type YipConfig struct {
|
type YipConfig struct {
|
||||||
|
@@ -1,16 +1,22 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rancher/os2/pkg/dmidecode"
|
||||||
|
"github.com/rancher/rancherd/pkg/tpm"
|
||||||
values "github.com/rancher/wrangler/pkg/data"
|
values "github.com/rancher/wrangler/pkg/data"
|
||||||
"github.com/rancher/wrangler/pkg/data/convert"
|
"github.com/rancher/wrangler/pkg/data/convert"
|
||||||
schemas2 "github.com/rancher/wrangler/pkg/schemas"
|
schemas2 "github.com/rancher/wrangler/pkg/schemas"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -60,30 +66,33 @@ func readFileFunc(path string) func() (map[string]interface{}, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func readNested(data map[string]interface{}) (map[string]interface{}, error) {
|
func readNested(data map[string]interface{}, overlay bool) (map[string]interface{}, error) {
|
||||||
var (
|
var (
|
||||||
nestedConfigFiles = convert.ToStringSlice(values.GetValueN(data, "rancheros", "install", "configUrl"))
|
nestedConfigFiles = convert.ToStringSlice(values.GetValueN(data, "rancheros", "install", "configUrl"))
|
||||||
funcs []reader
|
funcs []reader
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if overlay {
|
||||||
|
funcs = append(funcs, func() (map[string]interface{}, error) {
|
||||||
|
return data, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
for _, nestedConfigFile := range nestedConfigFiles {
|
for _, nestedConfigFile := range nestedConfigFiles {
|
||||||
funcs = append(funcs, readFileFunc(nestedConfigFile))
|
funcs = append(funcs, readFileFunc(nestedConfigFile))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !overlay {
|
||||||
funcs = append(funcs, func() (map[string]interface{}, error) {
|
funcs = append(funcs, func() (map[string]interface{}, error) {
|
||||||
return data, nil
|
return data, nil
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return merge(funcs...)
|
return merge(funcs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func readFile(path string) (result map[string]interface{}, _ error) {
|
func readFile(path string) (result map[string]interface{}, _ error) {
|
||||||
result = map[string]interface{}{}
|
result = map[string]interface{}{}
|
||||||
defer func() {
|
|
||||||
if v, ok := result["install"]; ok {
|
|
||||||
values.PutValue(result, v, "rancheros", "install")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(path, "http://"):
|
case strings.HasPrefix(path, "http://"):
|
||||||
@@ -117,7 +126,7 @@ func readFile(path string) (result map[string]interface{}, _ error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return readNested(data)
|
return readNested(data, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
type reader func() (map[string]interface{}, error)
|
type reader func() (map[string]interface{}, error)
|
||||||
@@ -145,6 +154,20 @@ func readConfigMap(cfg string) (map[string]interface{}, error) {
|
|||||||
if cfg != "" {
|
if cfg != "" {
|
||||||
values.PutValue(data, cfg, "rancheros", "install", "configUrl")
|
values.PutValue(data, cfg, "rancheros", "install", "configUrl")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registrationURL := convert.ToString(values.GetValueN(data, "rancheros", "install", "registrationUrl"))
|
||||||
|
registrationCA := convert.ToString(values.GetValueN(data, "rancheros", "install", "registrationCaCert"))
|
||||||
|
if registrationURL != "" {
|
||||||
|
for {
|
||||||
|
newData, err := returnRegistrationData(registrationURL, registrationCA)
|
||||||
|
if err == nil {
|
||||||
|
return newData, nil
|
||||||
|
}
|
||||||
|
logrus.Errorf("failed to read registration URL %s, retrying: %v", registrationURL, err)
|
||||||
|
time.Sleep(15 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,12 +180,7 @@ func ToFile(cfg Config, output string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ToBytes(cfg Config) ([]byte, error) {
|
func ToBytes(cfg Config) ([]byte, error) {
|
||||||
data, err := merge(readFileFunc(cfg.RancherOS.Install.ConfigURL), func() (map[string]interface{}, error) {
|
data := values.MergeMaps(nil, cfg.Data)
|
||||||
return convert.EncodeToMap(cfg)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
values.RemoveValue(data, "install")
|
values.RemoveValue(data, "install")
|
||||||
values.RemoveValue(data, "rancheros", "install")
|
values.RemoveValue(data, "rancheros", "install")
|
||||||
bytes, err := yaml.Marshal(data)
|
bytes, err := yaml.Marshal(data)
|
||||||
@@ -179,7 +197,41 @@ func ReadConfig(cfg string) (result Config, err error) {
|
|||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, convert.ToObj(data, &result)
|
if err := convert.ToObj(data, &result); err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Data = data
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func returnRegistrationData(url, ca string) (map[string]interface{}, error) {
|
||||||
|
smbios, err := getSMBiosHeaders()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data, err := tpm.Get([]byte(ca), url, smbios)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
logrus.Infof("Retrieved config from registrationURL: %s", data)
|
||||||
|
result := map[string]interface{}{}
|
||||||
|
return result, json.Unmarshal(data, &result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSMBiosHeaders() (http.Header, error) {
|
||||||
|
smbios, err := dmidecode.Decode()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
smbiosData, err := json.Marshal(smbios)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
header := http.Header{}
|
||||||
|
header.Set("X-Cattle-Smbios", base64.StdEncoding.EncodeToString(smbiosData))
|
||||||
|
return header, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readCmdline() (map[string]interface{}, error) {
|
func readCmdline() (map[string]interface{}, error) {
|
||||||
@@ -221,5 +273,9 @@ func readCmdline() (map[string]interface{}, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return readNested(data)
|
if err := schema.Mapper.ToInternal(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return readNested(data, true)
|
||||||
}
|
}
|
||||||
|
@@ -16,7 +16,7 @@ type FuzzyNames struct {
|
|||||||
|
|
||||||
func (f *FuzzyNames) ToInternal(data data.Object) error {
|
func (f *FuzzyNames) ToInternal(data data.Object) error {
|
||||||
for k, v := range data {
|
for k, v := range data {
|
||||||
if newK, ok := f.names[k]; ok && newK != k {
|
if newK, ok := f.names[strings.ToLower(k)]; ok && newK != k {
|
||||||
data[newK] = v
|
data[newK] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -43,7 +43,7 @@ func (h *handler) OnMachineInventoryRemove(key string, machine *v1.MachineInvent
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) OnMachineInventory(machine *v1.MachineInventory, status v1.MachineInventoryStatus) (v1.MachineInventoryStatus, error) {
|
func (h *handler) OnMachineInventory(machine *v1.MachineInventory, status v1.MachineInventoryStatus) (v1.MachineInventoryStatus, error) {
|
||||||
if machine == nil {
|
if machine == nil || machine.Spec.ClusterName == "" {
|
||||||
return status, nil
|
return status, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ func (h *handler) OnMachineInventory(machine *v1.MachineInventory, status v1.Mac
|
|||||||
return status, fmt.Errorf("waiting for mgmt cluster to be created for prov cluster %s/%s", machine.Namespace, machine.Spec.ClusterName)
|
return status, fmt.Errorf("waiting for mgmt cluster to be created for prov cluster %s/%s", machine.Namespace, machine.Spec.ClusterName)
|
||||||
}
|
}
|
||||||
|
|
||||||
crtName := name.SafeConcatName(cluster.Status.ClusterName, machine.Name, "-token")
|
crtName := name.SafeConcatName(cluster.Status.ClusterName, machine.Name, "token")
|
||||||
_, err = h.clusterRegistrationTokenCache.Get(cluster.Status.ClusterName, crtName)
|
_, err = h.clusterRegistrationTokenCache.Get(cluster.Status.ClusterName, crtName)
|
||||||
if apierrors.IsNotFound(err) {
|
if apierrors.IsNotFound(err) {
|
||||||
_, err = h.clusterRegistrationTokenClient.Create(&v3.ClusterRegistrationToken{
|
_, err = h.clusterRegistrationTokenClient.Create(&v3.ClusterRegistrationToken{
|
||||||
|
52
pkg/controllers/registration/registration.go
Normal file
52
pkg/controllers/registration/registration.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package registration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
v1 "github.com/rancher/os2/pkg/apis/rancheros.cattle.io/v1"
|
||||||
|
"github.com/rancher/os2/pkg/clients"
|
||||||
|
ranchercontrollers "github.com/rancher/os2/pkg/generated/controllers/management.cattle.io/v3"
|
||||||
|
roscontrollers "github.com/rancher/os2/pkg/generated/controllers/rancheros.cattle.io/v1"
|
||||||
|
"github.com/rancher/wrangler/pkg/randomtoken"
|
||||||
|
)
|
||||||
|
|
||||||
|
type handler struct {
|
||||||
|
settingsCache ranchercontrollers.SettingCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func Register(ctx context.Context, clients *clients.Clients) {
|
||||||
|
h := handler{
|
||||||
|
settingsCache: clients.Rancher.Setting().Cache(),
|
||||||
|
}
|
||||||
|
roscontrollers.RegisterMachineRegistrationStatusHandler(ctx, clients.OS.MachineRegistration(), "Ready", "machine-registration",
|
||||||
|
h.OnChange)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) OnChange(obj *v1.MachineRegistration, status v1.MachineRegistrationStatus) (v1.MachineRegistrationStatus, error) {
|
||||||
|
serverURL, err := h.serverURL()
|
||||||
|
if err != nil {
|
||||||
|
return status, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if status.RegistrationToken == "" {
|
||||||
|
status.RegistrationToken, err = randomtoken.Generate()
|
||||||
|
if err != nil {
|
||||||
|
return status, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status.RegistrationURL = serverURL + "/v1-rancheros/registration/" + status.RegistrationToken
|
||||||
|
return status, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) serverURL() (string, error) {
|
||||||
|
setting, err := h.settingsCache.Get("server-url")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if setting.Value == "" {
|
||||||
|
return "", fmt.Errorf("server-url is not set")
|
||||||
|
}
|
||||||
|
return setting.Value, nil
|
||||||
|
}
|
78
pkg/dmidecode/decode.go
Normal file
78
pkg/dmidecode/decode.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package dmidecode
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
values "github.com/rancher/wrangler/pkg/data"
|
||||||
|
"github.com/rancher/wrangler/pkg/kv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Decode() (map[string]interface{}, error) {
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
cmd := exec.Command("dmidecode")
|
||||||
|
cmd.Stdout = buf
|
||||||
|
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return nil, fmt.Errorf("looking up SMBIOS tables (using dmidecode): %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dmiOutputToMap(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dmiOutputToMap(buf io.Reader) map[string]interface{} {
|
||||||
|
var (
|
||||||
|
result = map[string]interface{}{}
|
||||||
|
scanner = bufio.NewScanner(buf)
|
||||||
|
start = false
|
||||||
|
lastKey []string
|
||||||
|
stopLines = map[string]bool{
|
||||||
|
"OEM-specific Type": true,
|
||||||
|
"End Of Table": true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
if strings.HasPrefix(line, "Handle ") {
|
||||||
|
start = true
|
||||||
|
continue
|
||||||
|
} else if strings.TrimSpace(line) == "" || !start || stopLines[line] {
|
||||||
|
start = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var key []string
|
||||||
|
for strings.HasPrefix(line, "\t") {
|
||||||
|
line = strings.TrimPrefix(line, "\t")
|
||||||
|
if len(lastKey) > len(key) {
|
||||||
|
key = append(key, lastKey[len(key)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
name, value := kv.Split(line, ": ")
|
||||||
|
key = append(key, name)
|
||||||
|
|
||||||
|
if strings.TrimSpace(value) != "" || strings.Contains(line, ":") {
|
||||||
|
values.PutValue(result, value, key...)
|
||||||
|
} else if len(key) > 1 {
|
||||||
|
parentKey := key[:len(key)-1]
|
||||||
|
parentValue := values.GetValueN(result, parentKey...)
|
||||||
|
if parentSlice, ok := parentValue.([]interface{}); ok {
|
||||||
|
parentValue = append(parentSlice, name)
|
||||||
|
} else {
|
||||||
|
parentValue = []interface{}{name}
|
||||||
|
}
|
||||||
|
values.PutValue(result, parentValue, parentKey...)
|
||||||
|
} else {
|
||||||
|
values.PutValue(result, map[string]interface{}{}, key...)
|
||||||
|
}
|
||||||
|
|
||||||
|
lastKey = key
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
945
pkg/dmidecode/decode_test.go
Normal file
945
pkg/dmidecode/decode_test.go
Normal file
@@ -0,0 +1,945 @@
|
|||||||
|
package dmidecode
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gotest.tools/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testInput = `
|
||||||
|
# dmidecode 3.2
|
||||||
|
Getting SMBIOS data from sysfs.
|
||||||
|
SMBIOS 3.1.1 present.
|
||||||
|
Table at 0xACE76000.
|
||||||
|
|
||||||
|
Handle 0x0000, DMI type 222, 14 bytes
|
||||||
|
OEM-specific Type
|
||||||
|
Header and Data:
|
||||||
|
DE 0E 00 00 01 99 00 03 10 01 20 02 30 03
|
||||||
|
Strings:
|
||||||
|
Memory Init Complete
|
||||||
|
End of DXE Phase
|
||||||
|
BIOS Boot Complete
|
||||||
|
|
||||||
|
Handle 0x0001, DMI type 14, 8 bytes
|
||||||
|
Group Associations
|
||||||
|
Name: Intel(R) Silicon View Technology
|
||||||
|
Items: 1
|
||||||
|
0x0000 (OEM-specific)
|
||||||
|
|
||||||
|
Handle 0x0002, DMI type 134, 13 bytes
|
||||||
|
OEM-specific Type
|
||||||
|
Header and Data:
|
||||||
|
86 0D 02 00 21 02 20 20 00 00 00 00 00
|
||||||
|
|
||||||
|
Handle 0x0003, DMI type 16, 23 bytes
|
||||||
|
Physical Memory Array
|
||||||
|
Location: System Board Or Motherboard
|
||||||
|
Use: System Memory
|
||||||
|
Error Correction Type: None
|
||||||
|
Maximum Capacity: 64 GB
|
||||||
|
Error Information Handle: Not Provided
|
||||||
|
Number Of Devices: 4
|
||||||
|
|
||||||
|
Handle 0x0004, DMI type 17, 40 bytes
|
||||||
|
Memory Device
|
||||||
|
Array Handle: 0x0003
|
||||||
|
Error Information Handle: Not Provided
|
||||||
|
Total Width: 64 bits
|
||||||
|
Data Width: 64 bits
|
||||||
|
Size: 32 GB
|
||||||
|
Form Factor: SODIMM
|
||||||
|
Set: None
|
||||||
|
Locator: ChannelA-DIMM0
|
||||||
|
Bank Locator: BANK 0
|
||||||
|
Type: DDR4
|
||||||
|
Type Detail: Synchronous
|
||||||
|
Speed: 2667 MT/s
|
||||||
|
Manufacturer: Micron
|
||||||
|
Serial Number: 25CD9D0D
|
||||||
|
Asset Tag: None
|
||||||
|
Part Number: 16ATF4G64HZ-2G6B2
|
||||||
|
Rank: 2
|
||||||
|
Configured Memory Speed: 2667 MT/s
|
||||||
|
Minimum Voltage: Unknown
|
||||||
|
Maximum Voltage: Unknown
|
||||||
|
Configured Voltage: 1.2 V
|
||||||
|
|
||||||
|
Handle 0x0005, DMI type 17, 40 bytes
|
||||||
|
Memory Device
|
||||||
|
Array Handle: 0x0003
|
||||||
|
Error Information Handle: Not Provided
|
||||||
|
Total Width: 64 bits
|
||||||
|
Data Width: 64 bits
|
||||||
|
Size: 32 GB
|
||||||
|
Form Factor: SODIMM
|
||||||
|
Set: None
|
||||||
|
Locator: ChannelB-DIMM0
|
||||||
|
Bank Locator: BANK 2
|
||||||
|
Type: DDR4
|
||||||
|
Type Detail: Synchronous
|
||||||
|
Speed: 2667 MT/s
|
||||||
|
Manufacturer: Micron
|
||||||
|
Serial Number: XXXXXXXX
|
||||||
|
Asset Tag: None
|
||||||
|
Part Number: 16ATF4G64HZ-2G6B2
|
||||||
|
Rank: 2
|
||||||
|
Configured Memory Speed: 2667 MT/s
|
||||||
|
Minimum Voltage: Unknown
|
||||||
|
Maximum Voltage: Unknown
|
||||||
|
Configured Voltage: 1.2 V
|
||||||
|
|
||||||
|
Handle 0x0006, DMI type 19, 31 bytes
|
||||||
|
Memory Array Mapped Address
|
||||||
|
Starting Address: 0x00000000000
|
||||||
|
Ending Address: 0x00FFFFFFFFF
|
||||||
|
Range Size: 64 GB
|
||||||
|
Physical Array Handle: 0x0003
|
||||||
|
Partition Width: 2
|
||||||
|
|
||||||
|
Handle 0x0007, DMI type 221, 12 bytes
|
||||||
|
OEM-specific Type
|
||||||
|
Header and Data:
|
||||||
|
DD 0C 07 00 01 01 00 02 00 00 BD 10
|
||||||
|
Strings:
|
||||||
|
BIOS Guard
|
||||||
|
|
||||||
|
Handle 0x0008, DMI type 221, 26 bytes
|
||||||
|
OEM-specific Type
|
||||||
|
Header and Data:
|
||||||
|
DD 1A 08 00 03 01 00 07 00 68 40 00 02 00 00 00
|
||||||
|
00 D6 00 03 00 01 06 00 00 00
|
||||||
|
Strings:
|
||||||
|
Reference Code - CPU
|
||||||
|
uCode Version
|
||||||
|
TXT ACM version
|
||||||
|
|
||||||
|
Handle 0x0009, DMI type 221, 26 bytes
|
||||||
|
OEM-specific Type
|
||||||
|
Header and Data:
|
||||||
|
DD 1A 09 00 03 01 00 07 00 68 40 00 02 00 0C 00
|
||||||
|
00 0A 00 03 04 0C 00 46 74 06
|
||||||
|
Strings:
|
||||||
|
Reference Code - ME
|
||||||
|
MEBx version
|
||||||
|
ME Firmware Version
|
||||||
|
Corporate SKU
|
||||||
|
|
||||||
|
Handle 0x000A, DMI type 221, 82 bytes
|
||||||
|
OEM-specific Type
|
||||||
|
Header and Data:
|
||||||
|
DD 52 0A 00 0B 01 00 07 00 68 40 00 02 03 FF FF
|
||||||
|
FF FF FF 04 00 FF FF FF 10 00 05 00 FF FF FF 10
|
||||||
|
00 06 00 02 0A 00 00 00 07 00 02 00 00 00 00 08
|
||||||
|
00 09 00 00 00 00 09 00 0A 00 00 00 00 0A 00 07
|
||||||
|
00 00 00 00 0B 00 06 00 00 00 00 0C 00 07 00 00
|
||||||
|
00 00
|
||||||
|
Strings:
|
||||||
|
Reference Code - CNL PCH
|
||||||
|
PCH-CRID Status
|
||||||
|
Disabled
|
||||||
|
PCH-CRID Original Value
|
||||||
|
PCH-CRID New Value
|
||||||
|
OPROM - RST - RAID
|
||||||
|
CNL PCH H A0 Hsio Version
|
||||||
|
CNL PCH H Ax Hsio Version
|
||||||
|
CNL PCH H Bx Hsio Version
|
||||||
|
CNL PCH LP B0 Hsio Version
|
||||||
|
CNL PCH LP Bx Hsio Version
|
||||||
|
CNL PCH LP Dx Hsio Version
|
||||||
|
|
||||||
|
Handle 0x000B, DMI type 221, 54 bytes
|
||||||
|
OEM-specific Type
|
||||||
|
Header and Data:
|
||||||
|
DD 36 0B 00 07 01 00 07 00 68 40 00 02 00 00 07
|
||||||
|
01 6E 00 03 00 07 00 68 40 00 04 05 FF FF FF FF
|
||||||
|
FF 06 00 00 00 00 0D 00 07 00 00 00 00 0D 00 08
|
||||||
|
00 FF FF FF FF FF
|
||||||
|
Strings:
|
||||||
|
Reference Code - SA - System Agent
|
||||||
|
Reference Code - MRC
|
||||||
|
SA - PCIe Version
|
||||||
|
SA-CRID Status
|
||||||
|
Enabled
|
||||||
|
SA-CRID Original Value
|
||||||
|
SA-CRID New Value
|
||||||
|
OPROM - VBIOS
|
||||||
|
|
||||||
|
Handle 0x000C, DMI type 221, 12 bytes
|
||||||
|
OEM-specific Type
|
||||||
|
Header and Data:
|
||||||
|
DD 0C 0C 00 01 01 00 04 00 00 00 00
|
||||||
|
Strings:
|
||||||
|
FSP Binary Version
|
||||||
|
|
||||||
|
Handle 0x000D, DMI type 7, 27 bytes
|
||||||
|
Cache Information
|
||||||
|
Socket Designation: L1 Cache
|
||||||
|
Configuration: Enabled, Not Socketed, Level 1
|
||||||
|
Operational Mode: Write Back
|
||||||
|
Location: Internal
|
||||||
|
Installed Size: 512 kB
|
||||||
|
Maximum Size: 512 kB
|
||||||
|
Supported SRAM Types:
|
||||||
|
Synchronous
|
||||||
|
Installed SRAM Type: Synchronous
|
||||||
|
Speed: Unknown
|
||||||
|
Error Correction Type: Parity
|
||||||
|
System Type: Unified
|
||||||
|
Associativity: 8-way Set-associative
|
||||||
|
|
||||||
|
Handle 0x000E, DMI type 7, 27 bytes
|
||||||
|
Cache Information
|
||||||
|
Socket Designation: L2 Cache
|
||||||
|
Configuration: Enabled, Not Socketed, Level 2
|
||||||
|
Operational Mode: Write Back
|
||||||
|
Location: Internal
|
||||||
|
Installed Size: 2048 kB
|
||||||
|
Maximum Size: 2048 kB
|
||||||
|
Supported SRAM Types:
|
||||||
|
Synchronous
|
||||||
|
Installed SRAM Type: Synchronous
|
||||||
|
Speed: Unknown
|
||||||
|
Error Correction Type: Single-bit ECC
|
||||||
|
System Type: Unified
|
||||||
|
Associativity: 4-way Set-associative
|
||||||
|
|
||||||
|
Handle 0x000F, DMI type 7, 27 bytes
|
||||||
|
Cache Information
|
||||||
|
Socket Designation: L3 Cache
|
||||||
|
Configuration: Enabled, Not Socketed, Level 3
|
||||||
|
Operational Mode: Write Back
|
||||||
|
Location: Internal
|
||||||
|
Installed Size: 16384 kB
|
||||||
|
Maximum Size: 16384 kB
|
||||||
|
Supported SRAM Types:
|
||||||
|
Synchronous
|
||||||
|
Installed SRAM Type: Synchronous
|
||||||
|
Speed: Unknown
|
||||||
|
Error Correction Type: Multi-bit ECC
|
||||||
|
System Type: Unified
|
||||||
|
Associativity: 16-way Set-associative
|
||||||
|
|
||||||
|
Handle 0x0010, DMI type 4, 48 bytes
|
||||||
|
Processor Information
|
||||||
|
Socket Designation: U3E1
|
||||||
|
Type: Central Processor
|
||||||
|
Family: Core i9
|
||||||
|
Manufacturer: Intel(R) Corporation
|
||||||
|
ID: ED 06 09 00 FF FB EB BF
|
||||||
|
Signature: Type 0, Family 6, Model 158, Stepping 13
|
||||||
|
Flags:
|
||||||
|
FPU (Floating-point unit on-chip)
|
||||||
|
VME (Virtual mode extension)
|
||||||
|
DE (Debugging extension)
|
||||||
|
PSE (Page size extension)
|
||||||
|
TSC (Time stamp counter)
|
||||||
|
MSR (Model specific registers)
|
||||||
|
PAE (Physical address extension)
|
||||||
|
MCE (Machine check exception)
|
||||||
|
CX8 (CMPXCHG8 instruction supported)
|
||||||
|
APIC (On-chip APIC hardware supported)
|
||||||
|
SEP (Fast system call)
|
||||||
|
MTRR (Memory type range registers)
|
||||||
|
PGE (Page global enable)
|
||||||
|
MCA (Machine check architecture)
|
||||||
|
CMOV (Conditional move instruction supported)
|
||||||
|
PAT (Page attribute table)
|
||||||
|
PSE-36 (36-bit page size extension)
|
||||||
|
CLFSH (CLFLUSH instruction supported)
|
||||||
|
DS (Debug store)
|
||||||
|
ACPI (ACPI supported)
|
||||||
|
MMX (MMX technology supported)
|
||||||
|
FXSR (FXSAVE and FXSTOR instructions supported)
|
||||||
|
SSE (Streaming SIMD extensions)
|
||||||
|
SSE2 (Streaming SIMD extensions 2)
|
||||||
|
SS (Self-snoop)
|
||||||
|
HTT (Multi-threading)
|
||||||
|
TM (Thermal monitor supported)
|
||||||
|
PBE (Pending break enabled)
|
||||||
|
Version: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
|
||||||
|
Voltage: 0.8 V
|
||||||
|
External Clock: 100 MHz
|
||||||
|
Max Speed: 2300 MHz
|
||||||
|
Current Speed: 2300 MHz
|
||||||
|
Status: Populated, Enabled
|
||||||
|
Upgrade: Socket BGA1440
|
||||||
|
L1 Cache Handle: 0x000D
|
||||||
|
L2 Cache Handle: 0x000E
|
||||||
|
L3 Cache Handle: 0x000F
|
||||||
|
Serial Number: None
|
||||||
|
Asset Tag: None
|
||||||
|
Part Number: None
|
||||||
|
Core Count: 8
|
||||||
|
Core Enabled: 8
|
||||||
|
Thread Count: 16
|
||||||
|
Characteristics:
|
||||||
|
64-bit capable
|
||||||
|
Multi-Core
|
||||||
|
Hardware Thread
|
||||||
|
Execute Protection
|
||||||
|
Enhanced Virtualization
|
||||||
|
Power/Performance Control
|
||||||
|
|
||||||
|
Handle 0x0011, DMI type 0, 26 bytes
|
||||||
|
BIOS Information
|
||||||
|
Vendor: LENOVO
|
||||||
|
Version: N2OET47W (1.34 )
|
||||||
|
Release Date: 08/06/2020
|
||||||
|
Address: 0xE0000
|
||||||
|
Runtime Size: 128 kB
|
||||||
|
ROM Size: 32 MB
|
||||||
|
Characteristics:
|
||||||
|
PCI is supported
|
||||||
|
PNP is supported
|
||||||
|
BIOS is upgradeable
|
||||||
|
BIOS shadowing is allowed
|
||||||
|
Boot from CD is supported
|
||||||
|
Selectable boot is supported
|
||||||
|
EDD is supported
|
||||||
|
3.5"/720 kB floppy services are supported (int 13h)
|
||||||
|
Print screen service is supported (int 5h)
|
||||||
|
8042 keyboard services are supported (int 9h)
|
||||||
|
Serial services are supported (int 14h)
|
||||||
|
Printer services are supported (int 17h)
|
||||||
|
CGA/mono video services are supported (int 10h)
|
||||||
|
ACPI is supported
|
||||||
|
USB legacy is supported
|
||||||
|
BIOS boot specification is supported
|
||||||
|
Targeted content distribution is supported
|
||||||
|
UEFI is supported
|
||||||
|
BIOS Revision: 1.34
|
||||||
|
Firmware Revision: 1.23
|
||||||
|
|
||||||
|
Handle 0x0012, DMI type 1, 27 bytes
|
||||||
|
System Information
|
||||||
|
Manufacturer: LENOVO
|
||||||
|
Product Name: 20QTS00Y00
|
||||||
|
Version: ThinkPad P1 Gen 2
|
||||||
|
Serial Number: XXXXXXXX
|
||||||
|
UUID: 069a9e4c-2eec-11b2-a85c-e9c6a8e86998
|
||||||
|
Wake-up Type: Power Switch
|
||||||
|
SKU Number: LENOVO_MT_20QT_BU_Think_FM_ThinkPad P1 Gen 2
|
||||||
|
Family: ThinkPad P1 Gen 2
|
||||||
|
|
||||||
|
Handle 0x0013, DMI type 2, 15 bytes
|
||||||
|
Base Board Information
|
||||||
|
Manufacturer: LENOVO
|
||||||
|
Product Name: 20QTS00Y00
|
||||||
|
Version: SDK0T08861 WIN
|
||||||
|
Serial Number: XXXXXXXX02W
|
||||||
|
Asset Tag: Not Available
|
||||||
|
Features:
|
||||||
|
Board is a hosting board
|
||||||
|
Board is replaceable
|
||||||
|
Location In Chassis: Not Available
|
||||||
|
Chassis Handle: 0x0000
|
||||||
|
Type: Motherboard
|
||||||
|
Contained Object Handles: 0
|
||||||
|
|
||||||
|
Handle 0x0014, DMI type 3, 22 bytes
|
||||||
|
Chassis Information
|
||||||
|
Manufacturer: LENOVO
|
||||||
|
Type: Notebook
|
||||||
|
Lock: Not Present
|
||||||
|
Version: None
|
||||||
|
Serial Number: XXXXXXXX
|
||||||
|
Asset Tag: No Asset Information
|
||||||
|
Boot-up State: Unknown
|
||||||
|
Power Supply State: Unknown
|
||||||
|
Thermal State: Unknown
|
||||||
|
Security Status: Unknown
|
||||||
|
OEM Information: 0x00000000
|
||||||
|
Height: Unspecified
|
||||||
|
Number Of Power Cords: Unspecified
|
||||||
|
Contained Elements: 0
|
||||||
|
SKU Number: Not Specified
|
||||||
|
|
||||||
|
Handle 0x0015, DMI type 8, 9 bytes
|
||||||
|
Port Connector Information
|
||||||
|
Internal Reference Designator: Not Available
|
||||||
|
Internal Connector Type: None
|
||||||
|
External Reference Designator: USB 1
|
||||||
|
External Connector Type: Access Bus (USB)
|
||||||
|
Port Type: USB
|
||||||
|
|
||||||
|
Handle 0x0016, DMI type 8, 9 bytes
|
||||||
|
Port Connector Information
|
||||||
|
Internal Reference Designator: Not Available
|
||||||
|
Internal Connector Type: None
|
||||||
|
External Reference Designator: USB 2
|
||||||
|
External Connector Type: Access Bus (USB)
|
||||||
|
Port Type: USB
|
||||||
|
|
||||||
|
Handle 0x0017, DMI type 8, 9 bytes
|
||||||
|
Port Connector Information
|
||||||
|
Internal Reference Designator: Not Available
|
||||||
|
Internal Connector Type: None
|
||||||
|
External Reference Designator: USB 3
|
||||||
|
External Connector Type: Access Bus (USB)
|
||||||
|
Port Type: USB
|
||||||
|
|
||||||
|
Handle 0x0018, DMI type 8, 9 bytes
|
||||||
|
Port Connector Information
|
||||||
|
Internal Reference Designator: Not Available
|
||||||
|
Internal Connector Type: None
|
||||||
|
External Reference Designator: USB 4
|
||||||
|
External Connector Type: Access Bus (USB)
|
||||||
|
Port Type: USB
|
||||||
|
|
||||||
|
Handle 0x0019, DMI type 126, 9 bytes
|
||||||
|
Inactive
|
||||||
|
|
||||||
|
Handle 0x001A, DMI type 126, 9 bytes
|
||||||
|
Inactive
|
||||||
|
|
||||||
|
Handle 0x001B, DMI type 126, 9 bytes
|
||||||
|
Inactive
|
||||||
|
|
||||||
|
Handle 0x001C, DMI type 126, 9 bytes
|
||||||
|
Inactive
|
||||||
|
|
||||||
|
Handle 0x001D, DMI type 126, 9 bytes
|
||||||
|
Inactive
|
||||||
|
|
||||||
|
Handle 0x001E, DMI type 8, 9 bytes
|
||||||
|
Port Connector Information
|
||||||
|
Internal Reference Designator: Not Available
|
||||||
|
Internal Connector Type: None
|
||||||
|
External Reference Designator: Ethernet
|
||||||
|
External Connector Type: RJ-45
|
||||||
|
Port Type: Network Port
|
||||||
|
|
||||||
|
Handle 0x001F, DMI type 126, 9 bytes
|
||||||
|
Inactive
|
||||||
|
|
||||||
|
Handle 0x0020, DMI type 126, 9 bytes
|
||||||
|
Inactive
|
||||||
|
|
||||||
|
Handle 0x0021, DMI type 8, 9 bytes
|
||||||
|
Port Connector Information
|
||||||
|
Internal Reference Designator: Not Available
|
||||||
|
Internal Connector Type: None
|
||||||
|
External Reference Designator: Hdmi1
|
||||||
|
External Connector Type: Other
|
||||||
|
Port Type: Video Port
|
||||||
|
|
||||||
|
Handle 0x0022, DMI type 126, 9 bytes
|
||||||
|
Inactive
|
||||||
|
|
||||||
|
Handle 0x0023, DMI type 126, 9 bytes
|
||||||
|
Inactive
|
||||||
|
|
||||||
|
Handle 0x0024, DMI type 126, 9 bytes
|
||||||
|
Inactive
|
||||||
|
|
||||||
|
Handle 0x0025, DMI type 126, 9 bytes
|
||||||
|
Inactive
|
||||||
|
|
||||||
|
Handle 0x0026, DMI type 8, 9 bytes
|
||||||
|
Port Connector Information
|
||||||
|
Internal Reference Designator: Not Available
|
||||||
|
Internal Connector Type: None
|
||||||
|
External Reference Designator: Headphone/Microphone Combo Jack1
|
||||||
|
External Connector Type: Mini Jack (headphones)
|
||||||
|
Port Type: Audio Port
|
||||||
|
|
||||||
|
Handle 0x0027, DMI type 126, 9 bytes
|
||||||
|
Inactive
|
||||||
|
|
||||||
|
Handle 0x0028, DMI type 9, 17 bytes
|
||||||
|
System Slot Information
|
||||||
|
Designation: Media Card Slot
|
||||||
|
Type: Other
|
||||||
|
Current Usage: Available
|
||||||
|
Length: Other
|
||||||
|
Characteristics:
|
||||||
|
Hot-plug devices are supported
|
||||||
|
Bus Address: 00ff:ff:1f.7
|
||||||
|
|
||||||
|
Handle 0x0029, DMI type 12, 5 bytes
|
||||||
|
System Configuration Options
|
||||||
|
|
||||||
|
Handle 0x002A, DMI type 13, 22 bytes
|
||||||
|
BIOS Language Information
|
||||||
|
Language Description Format: Abbreviated
|
||||||
|
Installable Languages: 1
|
||||||
|
en-US
|
||||||
|
Currently Installed Language: en-US
|
||||||
|
|
||||||
|
Handle 0x002B, DMI type 22, 26 bytes
|
||||||
|
Portable Battery
|
||||||
|
Location: Front
|
||||||
|
Manufacturer: SMP
|
||||||
|
Name: 01YU911
|
||||||
|
Design Capacity: 80400 mWh
|
||||||
|
Design Voltage: 15360 mV
|
||||||
|
SBDS Version: 03.01
|
||||||
|
Maximum Error: Unknown
|
||||||
|
SBDS Serial Number: 09E9
|
||||||
|
SBDS Manufacture Date: 2019-12-10
|
||||||
|
SBDS Chemistry: LiP
|
||||||
|
OEM-specific Information: 0x00000000
|
||||||
|
|
||||||
|
Handle 0x002C, DMI type 126, 26 bytes
|
||||||
|
Inactive
|
||||||
|
|
||||||
|
Handle 0x002D, DMI type 140, 15 bytes
|
||||||
|
OEM-specific Type
|
||||||
|
Header and Data:
|
||||||
|
8C 0F 2D 00 4C 45 4E 4F 56 4F 0B 09 01 01 02
|
||||||
|
Strings:
|
||||||
|
1.34
|
||||||
|
1.34
|
||||||
|
|
||||||
|
Handle 0x002E, DMI type 133, 5 bytes
|
||||||
|
OEM-specific Type
|
||||||
|
Header and Data:
|
||||||
|
85 05 2E 00 01
|
||||||
|
Strings:
|
||||||
|
KHOIHGIUCCHHII
|
||||||
|
|
||||||
|
Handle 0x002F, DMI type 135, 19 bytes
|
||||||
|
OEM-specific Type
|
||||||
|
Header and Data:
|
||||||
|
87 13 2F 00 54 50 07 02 42 41 59 20 49 2F 4F 20
|
||||||
|
04 00 00
|
||||||
|
|
||||||
|
Handle 0x0030, DMI type 130, 20 bytes
|
||||||
|
OEM-specific Type
|
||||||
|
Header and Data:
|
||||||
|
82 14 30 00 24 41 4D 54 01 00 01 00 01 A5 AF 02
|
||||||
|
C0 00 00 00
|
||||||
|
|
||||||
|
Handle 0x0031, DMI type 131, 64 bytes
|
||||||
|
OEM-specific Type
|
||||||
|
Header and Data:
|
||||||
|
83 40 31 00 35 00 00 00 0C 00 00 00 00 00 0A 00
|
||||||
|
F8 00 0E A3 00 00 00 00 09 C0 00 00 00 00 0C 00
|
||||||
|
74 06 46 00 00 00 00 00 FE 00 BB 15 00 00 00 00
|
||||||
|
00 00 00 00 26 00 00 00 76 50 72 6F 00 00 00 00
|
||||||
|
|
||||||
|
Handle 0x0032, DMI type 24, 5 bytes
|
||||||
|
Hardware Security
|
||||||
|
Power-On Password Status: Disabled
|
||||||
|
Keyboard Password Status: Not Implemented
|
||||||
|
Administrator Password Status: Disabled
|
||||||
|
Front Panel Reset Status: Not Implemented
|
||||||
|
|
||||||
|
Handle 0x0033, DMI type 132, 7 bytes
|
||||||
|
OEM-specific Type
|
||||||
|
Header and Data:
|
||||||
|
84 07 33 00 01 C0 36
|
||||||
|
|
||||||
|
Handle 0x0034, DMI type 18, 23 bytes
|
||||||
|
32-bit Memory Error Information
|
||||||
|
Type: OK
|
||||||
|
Granularity: Unknown
|
||||||
|
Operation: Unknown
|
||||||
|
Vendor Syndrome: Unknown
|
||||||
|
Memory Array Address: Unknown
|
||||||
|
Device Address: Unknown
|
||||||
|
Resolution: Unknown
|
||||||
|
|
||||||
|
Handle 0x0035, DMI type 21, 7 bytes
|
||||||
|
Built-in Pointing Device
|
||||||
|
Type: Track Point
|
||||||
|
Interface: PS/2
|
||||||
|
Buttons: 3
|
||||||
|
|
||||||
|
Handle 0x0036, DMI type 21, 7 bytes
|
||||||
|
Built-in Pointing Device
|
||||||
|
Type: Touch Pad
|
||||||
|
Interface: PS/2
|
||||||
|
Buttons: 2
|
||||||
|
|
||||||
|
Handle 0x0037, DMI type 131, 22 bytes
|
||||||
|
ThinkVantage Technologies
|
||||||
|
Version: 1
|
||||||
|
Diagnostics: No
|
||||||
|
|
||||||
|
Handle 0x0038, DMI type 136, 6 bytes
|
||||||
|
OEM-specific Type
|
||||||
|
Header and Data:
|
||||||
|
88 06 38 00 5A 5A
|
||||||
|
|
||||||
|
Handle 0x0039, DMI type 15, 31 bytes
|
||||||
|
System Event Log
|
||||||
|
Area Length: 786 bytes
|
||||||
|
Header Start Offset: 0x0000
|
||||||
|
Header Length: 16 bytes
|
||||||
|
Data Start Offset: 0x0010
|
||||||
|
Access Method: General-purpose non-volatile data functions
|
||||||
|
Access Address: 0x00F0
|
||||||
|
Status: Valid, Not Full
|
||||||
|
Change Token: 0x00000030
|
||||||
|
Header Format: Type 1
|
||||||
|
Supported Log Type Descriptors: 4
|
||||||
|
Descriptor 1: POST error
|
||||||
|
Data Format 1: POST results bitmap
|
||||||
|
Descriptor 2: PCI system error
|
||||||
|
Data Format 2: None
|
||||||
|
Descriptor 3: System reconfigured
|
||||||
|
Data Format 3: None
|
||||||
|
Descriptor 4: Log area reset/cleared
|
||||||
|
Data Format 4: None
|
||||||
|
|
||||||
|
Handle 0x003A, DMI type 140, 19 bytes
|
||||||
|
OEM-specific Type
|
||||||
|
Header and Data:
|
||||||
|
8C 13 3A 00 4C 45 4E 4F 56 4F 0B 04 01 B2 00 4D
|
||||||
|
53 20 00
|
||||||
|
|
||||||
|
Handle 0x003B, DMI type 140, 19 bytes
|
||||||
|
OEM-specific Type
|
||||||
|
Header and Data:
|
||||||
|
8C 13 3B 00 4C 45 4E 4F 56 4F 0B 05 01 07 00 00
|
||||||
|
00 00 00
|
||||||
|
|
||||||
|
Handle 0x003C, DMI type 140, 23 bytes
|
||||||
|
OEM-specific Type
|
||||||
|
Header and Data:
|
||||||
|
8C 17 3C 00 4C 45 4E 4F 56 4F 0B 06 01 CB 06 51
|
||||||
|
74 01 60 00 00 00 00
|
||||||
|
|
||||||
|
Handle 0x003D, DMI type 14, 8 bytes
|
||||||
|
Group Associations
|
||||||
|
Name: $MEI
|
||||||
|
Items: 1
|
||||||
|
0x0000 (OEM-specific)
|
||||||
|
|
||||||
|
Handle 0x003E, DMI type 219, 106 bytes
|
||||||
|
OEM-specific Type
|
||||||
|
Header and Data:
|
||||||
|
DB 6A 3E 00 01 04 01 45 02 00 94 06 81 10 89 30
|
||||||
|
00 00 00 04 40 00 00 01 1F 00 00 C9 0A 40 C4 02
|
||||||
|
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
|
||||||
|
FF FF FF FF FF FF FF FF 03 00 00 00 80 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||||
|
00 00 00 00 00 00 00 00 00 00
|
||||||
|
Strings:
|
||||||
|
MEI1
|
||||||
|
MEI2
|
||||||
|
MEI3
|
||||||
|
MEI4
|
||||||
|
|
||||||
|
Handle 0x003F, DMI type 140, 15 bytes
|
||||||
|
ThinkPad Embedded Controller Program
|
||||||
|
Version ID: N2OHT36W
|
||||||
|
Release Date: 05/07/2020
|
||||||
|
|
||||||
|
Handle 0x0040, DMI type 140, 43 bytes
|
||||||
|
OEM-specific Type
|
||||||
|
Header and Data:
|
||||||
|
8C 2B 40 00 4C 45 4E 4F 56 4F 0B 08 01 03 21 08
|
||||||
|
05 18 39 03 21 05 19 18 04 03 21 05 19 17 56 03
|
||||||
|
21 05 19 17 54 03 21 05 19 17 48
|
||||||
|
|
||||||
|
Handle 0x0041, DMI type 135, 18 bytes
|
||||||
|
OEM-specific Type
|
||||||
|
Header and Data:
|
||||||
|
87 12 41 00 54 50 07 01 01 00 00 00 03 00 00 00
|
||||||
|
03 00
|
||||||
|
|
||||||
|
Handle 0xFEFF, DMI type 127, 4 bytes
|
||||||
|
End Of Table
|
||||||
|
`
|
||||||
|
|
||||||
|
var testOutput = `{
|
||||||
|
"32-bit Memory Error Information": {
|
||||||
|
"Device Address": "Unknown",
|
||||||
|
"Granularity": "Unknown",
|
||||||
|
"Memory Array Address": "Unknown",
|
||||||
|
"Operation": "Unknown",
|
||||||
|
"Resolution": "Unknown",
|
||||||
|
"Type": "OK",
|
||||||
|
"Vendor Syndrome": "Unknown"
|
||||||
|
},
|
||||||
|
"BIOS Information": {
|
||||||
|
"Address": "0xE0000",
|
||||||
|
"BIOS Revision": "1.34",
|
||||||
|
"Characteristics:": [
|
||||||
|
"PCI is supported",
|
||||||
|
"PNP is supported",
|
||||||
|
"BIOS is upgradeable",
|
||||||
|
"BIOS shadowing is allowed",
|
||||||
|
"Boot from CD is supported",
|
||||||
|
"Selectable boot is supported",
|
||||||
|
"EDD is supported",
|
||||||
|
"3.5\"/720 kB floppy services are supported (int 13h)",
|
||||||
|
"Print screen service is supported (int 5h)",
|
||||||
|
"8042 keyboard services are supported (int 9h)",
|
||||||
|
"Serial services are supported (int 14h)",
|
||||||
|
"Printer services are supported (int 17h)",
|
||||||
|
"CGA/mono video services are supported (int 10h)",
|
||||||
|
"ACPI is supported",
|
||||||
|
"USB legacy is supported",
|
||||||
|
"BIOS boot specification is supported",
|
||||||
|
"Targeted content distribution is supported",
|
||||||
|
"UEFI is supported"
|
||||||
|
],
|
||||||
|
"Firmware Revision": "1.23",
|
||||||
|
"ROM Size": "32 MB",
|
||||||
|
"Release Date": "08/06/2020",
|
||||||
|
"Runtime Size": "128 kB",
|
||||||
|
"Vendor": "LENOVO",
|
||||||
|
"Version": "N2OET47W (1.34 )"
|
||||||
|
},
|
||||||
|
"BIOS Language Information": {
|
||||||
|
"Currently Installed Language": "en-US",
|
||||||
|
"Installable Languages": [
|
||||||
|
"en-US"
|
||||||
|
],
|
||||||
|
"Language Description Format": "Abbreviated"
|
||||||
|
},
|
||||||
|
"Base Board Information": {
|
||||||
|
"Asset Tag": "Not Available",
|
||||||
|
"Chassis Handle": "0x0000",
|
||||||
|
"Contained Object Handles": "0",
|
||||||
|
"Features:": [
|
||||||
|
"Board is a hosting board",
|
||||||
|
"Board is replaceable"
|
||||||
|
],
|
||||||
|
"Location In Chassis": "Not Available",
|
||||||
|
"Manufacturer": "LENOVO",
|
||||||
|
"Product Name": "20QTS00Y00",
|
||||||
|
"Serial Number": "XXXXXXXX02W",
|
||||||
|
"Type": "Motherboard",
|
||||||
|
"Version": "SDK0T08861 WIN"
|
||||||
|
},
|
||||||
|
"Built-in Pointing Device": {
|
||||||
|
"Buttons": "2",
|
||||||
|
"Interface": "PS/2",
|
||||||
|
"Type": "Touch Pad"
|
||||||
|
},
|
||||||
|
"Cache Information": {
|
||||||
|
"Associativity": "16-way Set-associative",
|
||||||
|
"Configuration": "Enabled, Not Socketed, Level 3",
|
||||||
|
"Error Correction Type": "Multi-bit ECC",
|
||||||
|
"Installed SRAM Type": "Synchronous",
|
||||||
|
"Installed Size": "16384 kB",
|
||||||
|
"Location": "Internal",
|
||||||
|
"Maximum Size": "16384 kB",
|
||||||
|
"Operational Mode": "Write Back",
|
||||||
|
"Socket Designation": "L3 Cache",
|
||||||
|
"Speed": "Unknown",
|
||||||
|
"Supported SRAM Types:": [
|
||||||
|
"Synchronous"
|
||||||
|
],
|
||||||
|
"System Type": "Unified"
|
||||||
|
},
|
||||||
|
"Chassis Information": {
|
||||||
|
"Asset Tag": "No Asset Information",
|
||||||
|
"Boot-up State": "Unknown",
|
||||||
|
"Contained Elements": "0",
|
||||||
|
"Height": "Unspecified",
|
||||||
|
"Lock": "Not Present",
|
||||||
|
"Manufacturer": "LENOVO",
|
||||||
|
"Number Of Power Cords": "Unspecified",
|
||||||
|
"OEM Information": "0x00000000",
|
||||||
|
"Power Supply State": "Unknown",
|
||||||
|
"SKU Number": "Not Specified",
|
||||||
|
"Security Status": "Unknown",
|
||||||
|
"Serial Number": "XXXXXXXX",
|
||||||
|
"Thermal State": "Unknown",
|
||||||
|
"Type": "Notebook",
|
||||||
|
"Version": "None"
|
||||||
|
},
|
||||||
|
"Group Associations": {
|
||||||
|
"Items": [
|
||||||
|
"0x0000 (OEM-specific)"
|
||||||
|
],
|
||||||
|
"Name": "$MEI"
|
||||||
|
},
|
||||||
|
"Hardware Security": {
|
||||||
|
"Administrator Password Status": "Disabled",
|
||||||
|
"Front Panel Reset Status": "Not Implemented",
|
||||||
|
"Keyboard Password Status": "Not Implemented",
|
||||||
|
"Power-On Password Status": "Disabled"
|
||||||
|
},
|
||||||
|
"Inactive": {},
|
||||||
|
"Memory Array Mapped Address": {
|
||||||
|
"Ending Address": "0x00FFFFFFFFF",
|
||||||
|
"Partition Width": "2",
|
||||||
|
"Physical Array Handle": "0x0003",
|
||||||
|
"Range Size": "64 GB",
|
||||||
|
"Starting Address": "0x00000000000"
|
||||||
|
},
|
||||||
|
"Memory Device": {
|
||||||
|
"Array Handle": "0x0003",
|
||||||
|
"Asset Tag": "None",
|
||||||
|
"Bank Locator": "BANK 2",
|
||||||
|
"Configured Memory Speed": "2667 MT/s",
|
||||||
|
"Configured Voltage": "1.2 V",
|
||||||
|
"Data Width": "64 bits",
|
||||||
|
"Error Information Handle": "Not Provided",
|
||||||
|
"Form Factor": "SODIMM",
|
||||||
|
"Locator": "ChannelB-DIMM0",
|
||||||
|
"Manufacturer": "Micron",
|
||||||
|
"Maximum Voltage": "Unknown",
|
||||||
|
"Minimum Voltage": "Unknown",
|
||||||
|
"Part Number": "16ATF4G64HZ-2G6B2",
|
||||||
|
"Rank": "2",
|
||||||
|
"Serial Number": "XXXXXXXX",
|
||||||
|
"Set": "None",
|
||||||
|
"Size": "32 GB",
|
||||||
|
"Speed": "2667 MT/s",
|
||||||
|
"Total Width": "64 bits",
|
||||||
|
"Type": "DDR4",
|
||||||
|
"Type Detail": "Synchronous"
|
||||||
|
},
|
||||||
|
"Physical Memory Array": {
|
||||||
|
"Error Correction Type": "None",
|
||||||
|
"Error Information Handle": "Not Provided",
|
||||||
|
"Location": "System Board Or Motherboard",
|
||||||
|
"Maximum Capacity": "64 GB",
|
||||||
|
"Number Of Devices": "4",
|
||||||
|
"Use": "System Memory"
|
||||||
|
},
|
||||||
|
"Port Connector Information": {
|
||||||
|
"External Connector Type": "Mini Jack (headphones)",
|
||||||
|
"External Reference Designator": "Headphone/Microphone Combo Jack1",
|
||||||
|
"Internal Connector Type": "None",
|
||||||
|
"Internal Reference Designator": "Not Available",
|
||||||
|
"Port Type": "Audio Port"
|
||||||
|
},
|
||||||
|
"Portable Battery": {
|
||||||
|
"Design Capacity": "80400 mWh",
|
||||||
|
"Design Voltage": "15360 mV",
|
||||||
|
"Location": "Front",
|
||||||
|
"Manufacturer": "SMP",
|
||||||
|
"Maximum Error": "Unknown",
|
||||||
|
"Name": "01YU911",
|
||||||
|
"OEM-specific Information": "0x00000000",
|
||||||
|
"SBDS Chemistry": "LiP",
|
||||||
|
"SBDS Manufacture Date": "2019-12-10",
|
||||||
|
"SBDS Serial Number": "09E9",
|
||||||
|
"SBDS Version": "03.01"
|
||||||
|
},
|
||||||
|
"Processor Information": {
|
||||||
|
"Asset Tag": "None",
|
||||||
|
"Characteristics:": [
|
||||||
|
"64-bit capable",
|
||||||
|
"Multi-Core",
|
||||||
|
"Hardware Thread",
|
||||||
|
"Execute Protection",
|
||||||
|
"Enhanced Virtualization",
|
||||||
|
"Power/Performance Control"
|
||||||
|
],
|
||||||
|
"Core Count": "8",
|
||||||
|
"Core Enabled": "8",
|
||||||
|
"Current Speed": "2300 MHz",
|
||||||
|
"External Clock": "100 MHz",
|
||||||
|
"Family": "Core i9",
|
||||||
|
"Flags:": [
|
||||||
|
"FPU (Floating-point unit on-chip)",
|
||||||
|
"VME (Virtual mode extension)",
|
||||||
|
"DE (Debugging extension)",
|
||||||
|
"PSE (Page size extension)",
|
||||||
|
"TSC (Time stamp counter)",
|
||||||
|
"MSR (Model specific registers)",
|
||||||
|
"PAE (Physical address extension)",
|
||||||
|
"MCE (Machine check exception)",
|
||||||
|
"CX8 (CMPXCHG8 instruction supported)",
|
||||||
|
"APIC (On-chip APIC hardware supported)",
|
||||||
|
"SEP (Fast system call)",
|
||||||
|
"MTRR (Memory type range registers)",
|
||||||
|
"PGE (Page global enable)",
|
||||||
|
"MCA (Machine check architecture)",
|
||||||
|
"CMOV (Conditional move instruction supported)",
|
||||||
|
"PAT (Page attribute table)",
|
||||||
|
"PSE-36 (36-bit page size extension)",
|
||||||
|
"CLFSH (CLFLUSH instruction supported)",
|
||||||
|
"DS (Debug store)",
|
||||||
|
"ACPI (ACPI supported)",
|
||||||
|
"MMX (MMX technology supported)",
|
||||||
|
"FXSR (FXSAVE and FXSTOR instructions supported)",
|
||||||
|
"SSE (Streaming SIMD extensions)",
|
||||||
|
"SSE2 (Streaming SIMD extensions 2)",
|
||||||
|
"SS (Self-snoop)",
|
||||||
|
"HTT (Multi-threading)",
|
||||||
|
"TM (Thermal monitor supported)",
|
||||||
|
"PBE (Pending break enabled)"
|
||||||
|
],
|
||||||
|
"ID": "ED 06 09 00 FF FB EB BF",
|
||||||
|
"L1 Cache Handle": "0x000D",
|
||||||
|
"L2 Cache Handle": "0x000E",
|
||||||
|
"L3 Cache Handle": "0x000F",
|
||||||
|
"Manufacturer": "Intel(R) Corporation",
|
||||||
|
"Max Speed": "2300 MHz",
|
||||||
|
"Part Number": "None",
|
||||||
|
"Serial Number": "None",
|
||||||
|
"Signature": "Type 0, Family 6, Model 158, Stepping 13",
|
||||||
|
"Socket Designation": "U3E1",
|
||||||
|
"Status": "Populated, Enabled",
|
||||||
|
"Thread Count": "16",
|
||||||
|
"Type": "Central Processor",
|
||||||
|
"Upgrade": "Socket BGA1440",
|
||||||
|
"Version": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz",
|
||||||
|
"Voltage": "0.8 V"
|
||||||
|
},
|
||||||
|
"System Configuration Options": {},
|
||||||
|
"System Event Log": {
|
||||||
|
"Access Address": "0x00F0",
|
||||||
|
"Access Method": "General-purpose non-volatile data functions",
|
||||||
|
"Area Length": "786 bytes",
|
||||||
|
"Change Token": "0x00000030",
|
||||||
|
"Data Format 1": "POST results bitmap",
|
||||||
|
"Data Format 2": "None",
|
||||||
|
"Data Format 3": "None",
|
||||||
|
"Data Format 4": "None",
|
||||||
|
"Data Start Offset": "0x0010",
|
||||||
|
"Descriptor 1": "POST error",
|
||||||
|
"Descriptor 2": "PCI system error",
|
||||||
|
"Descriptor 3": "System reconfigured",
|
||||||
|
"Descriptor 4": "Log area reset/cleared",
|
||||||
|
"Header Format": "Type 1",
|
||||||
|
"Header Length": "16 bytes",
|
||||||
|
"Header Start Offset": "0x0000",
|
||||||
|
"Status": "Valid, Not Full",
|
||||||
|
"Supported Log Type Descriptors": "4"
|
||||||
|
},
|
||||||
|
"System Information": {
|
||||||
|
"Family": "ThinkPad P1 Gen 2",
|
||||||
|
"Manufacturer": "LENOVO",
|
||||||
|
"Product Name": "20QTS00Y00",
|
||||||
|
"SKU Number": "LENOVO_MT_20QT_BU_Think_FM_ThinkPad P1 Gen 2",
|
||||||
|
"Serial Number": "XXXXXXXX",
|
||||||
|
"UUID": "069a9e4c-2eec-11b2-a85c-e9c6a8e86998",
|
||||||
|
"Version": "ThinkPad P1 Gen 2",
|
||||||
|
"Wake-up Type": "Power Switch"
|
||||||
|
},
|
||||||
|
"System Slot Information": {
|
||||||
|
"Bus Address": "00ff:ff:1f.7",
|
||||||
|
"Characteristics:": [
|
||||||
|
"Hot-plug devices are supported"
|
||||||
|
],
|
||||||
|
"Current Usage": "Available",
|
||||||
|
"Designation": "Media Card Slot",
|
||||||
|
"Length": "Other",
|
||||||
|
"Type": "Other"
|
||||||
|
},
|
||||||
|
"ThinkPad Embedded Controller Program": {
|
||||||
|
"Release Date": "05/07/2020",
|
||||||
|
"Version ID": "N2OHT36W"
|
||||||
|
},
|
||||||
|
"ThinkVantage Technologies": {
|
||||||
|
"Diagnostics": "No",
|
||||||
|
"Version": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
func TestParse(t *testing.T) {
|
||||||
|
output := dmiOutputToMap(bytes.NewBufferString(testInput))
|
||||||
|
outputBuffer := bytes.NewBuffer(nil)
|
||||||
|
enc := json.NewEncoder(outputBuffer)
|
||||||
|
enc.SetIndent("", " ")
|
||||||
|
if err := enc.Encode(output); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, testOutput, outputBuffer.String())
|
||||||
|
}
|
@@ -31,6 +31,7 @@ func init() {
|
|||||||
|
|
||||||
type Interface interface {
|
type Interface interface {
|
||||||
MachineInventory() MachineInventoryController
|
MachineInventory() MachineInventoryController
|
||||||
|
MachineRegistration() MachineRegistrationController
|
||||||
ManagedOSImage() ManagedOSImageController
|
ManagedOSImage() ManagedOSImageController
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,6 +48,9 @@ type version struct {
|
|||||||
func (c *version) MachineInventory() MachineInventoryController {
|
func (c *version) MachineInventory() MachineInventoryController {
|
||||||
return NewMachineInventoryController(schema.GroupVersionKind{Group: "rancheros.cattle.io", Version: "v1", Kind: "MachineInventory"}, "machineinventories", true, c.controllerFactory)
|
return NewMachineInventoryController(schema.GroupVersionKind{Group: "rancheros.cattle.io", Version: "v1", Kind: "MachineInventory"}, "machineinventories", true, c.controllerFactory)
|
||||||
}
|
}
|
||||||
|
func (c *version) MachineRegistration() MachineRegistrationController {
|
||||||
|
return NewMachineRegistrationController(schema.GroupVersionKind{Group: "rancheros.cattle.io", Version: "v1", Kind: "MachineRegistration"}, "machineregistrations", true, c.controllerFactory)
|
||||||
|
}
|
||||||
func (c *version) ManagedOSImage() ManagedOSImageController {
|
func (c *version) ManagedOSImage() ManagedOSImageController {
|
||||||
return NewManagedOSImageController(schema.GroupVersionKind{Group: "rancheros.cattle.io", Version: "v1", Kind: "ManagedOSImage"}, "managedosimages", true, c.controllerFactory)
|
return NewManagedOSImageController(schema.GroupVersionKind{Group: "rancheros.cattle.io", Version: "v1", Kind: "ManagedOSImage"}, "managedosimages", true, c.controllerFactory)
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,376 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 Rancher Labs, Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by main. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rancher/lasso/pkg/client"
|
||||||
|
"github.com/rancher/lasso/pkg/controller"
|
||||||
|
v1 "github.com/rancher/os2/pkg/apis/rancheros.cattle.io/v1"
|
||||||
|
"github.com/rancher/wrangler/pkg/apply"
|
||||||
|
"github.com/rancher/wrangler/pkg/condition"
|
||||||
|
"github.com/rancher/wrangler/pkg/generic"
|
||||||
|
"github.com/rancher/wrangler/pkg/kv"
|
||||||
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MachineRegistrationHandler func(string, *v1.MachineRegistration) (*v1.MachineRegistration, error)
|
||||||
|
|
||||||
|
type MachineRegistrationController interface {
|
||||||
|
generic.ControllerMeta
|
||||||
|
MachineRegistrationClient
|
||||||
|
|
||||||
|
OnChange(ctx context.Context, name string, sync MachineRegistrationHandler)
|
||||||
|
OnRemove(ctx context.Context, name string, sync MachineRegistrationHandler)
|
||||||
|
Enqueue(namespace, name string)
|
||||||
|
EnqueueAfter(namespace, name string, duration time.Duration)
|
||||||
|
|
||||||
|
Cache() MachineRegistrationCache
|
||||||
|
}
|
||||||
|
|
||||||
|
type MachineRegistrationClient interface {
|
||||||
|
Create(*v1.MachineRegistration) (*v1.MachineRegistration, error)
|
||||||
|
Update(*v1.MachineRegistration) (*v1.MachineRegistration, error)
|
||||||
|
UpdateStatus(*v1.MachineRegistration) (*v1.MachineRegistration, error)
|
||||||
|
Delete(namespace, name string, options *metav1.DeleteOptions) error
|
||||||
|
Get(namespace, name string, options metav1.GetOptions) (*v1.MachineRegistration, error)
|
||||||
|
List(namespace string, opts metav1.ListOptions) (*v1.MachineRegistrationList, error)
|
||||||
|
Watch(namespace string, opts metav1.ListOptions) (watch.Interface, error)
|
||||||
|
Patch(namespace, name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.MachineRegistration, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MachineRegistrationCache interface {
|
||||||
|
Get(namespace, name string) (*v1.MachineRegistration, error)
|
||||||
|
List(namespace string, selector labels.Selector) ([]*v1.MachineRegistration, error)
|
||||||
|
|
||||||
|
AddIndexer(indexName string, indexer MachineRegistrationIndexer)
|
||||||
|
GetByIndex(indexName, key string) ([]*v1.MachineRegistration, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MachineRegistrationIndexer func(obj *v1.MachineRegistration) ([]string, error)
|
||||||
|
|
||||||
|
type machineRegistrationController struct {
|
||||||
|
controller controller.SharedController
|
||||||
|
client *client.Client
|
||||||
|
gvk schema.GroupVersionKind
|
||||||
|
groupResource schema.GroupResource
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMachineRegistrationController(gvk schema.GroupVersionKind, resource string, namespaced bool, controller controller.SharedControllerFactory) MachineRegistrationController {
|
||||||
|
c := controller.ForResourceKind(gvk.GroupVersion().WithResource(resource), gvk.Kind, namespaced)
|
||||||
|
return &machineRegistrationController{
|
||||||
|
controller: c,
|
||||||
|
client: c.Client(),
|
||||||
|
gvk: gvk,
|
||||||
|
groupResource: schema.GroupResource{
|
||||||
|
Group: gvk.Group,
|
||||||
|
Resource: resource,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromMachineRegistrationHandlerToHandler(sync MachineRegistrationHandler) generic.Handler {
|
||||||
|
return func(key string, obj runtime.Object) (ret runtime.Object, err error) {
|
||||||
|
var v *v1.MachineRegistration
|
||||||
|
if obj == nil {
|
||||||
|
v, err = sync(key, nil)
|
||||||
|
} else {
|
||||||
|
v, err = sync(key, obj.(*v1.MachineRegistration))
|
||||||
|
}
|
||||||
|
if v == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationController) Updater() generic.Updater {
|
||||||
|
return func(obj runtime.Object) (runtime.Object, error) {
|
||||||
|
newObj, err := c.Update(obj.(*v1.MachineRegistration))
|
||||||
|
if newObj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newObj, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateMachineRegistrationDeepCopyOnChange(client MachineRegistrationClient, obj *v1.MachineRegistration, handler func(obj *v1.MachineRegistration) (*v1.MachineRegistration, error)) (*v1.MachineRegistration, error) {
|
||||||
|
if obj == nil {
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
copyObj := obj.DeepCopy()
|
||||||
|
newObj, err := handler(copyObj)
|
||||||
|
if newObj != nil {
|
||||||
|
copyObj = newObj
|
||||||
|
}
|
||||||
|
if obj.ResourceVersion == copyObj.ResourceVersion && !equality.Semantic.DeepEqual(obj, copyObj) {
|
||||||
|
return client.Update(copyObj)
|
||||||
|
}
|
||||||
|
|
||||||
|
return copyObj, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationController) AddGenericHandler(ctx context.Context, name string, handler generic.Handler) {
|
||||||
|
c.controller.RegisterHandler(ctx, name, controller.SharedControllerHandlerFunc(handler))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationController) AddGenericRemoveHandler(ctx context.Context, name string, handler generic.Handler) {
|
||||||
|
c.AddGenericHandler(ctx, name, generic.NewRemoveHandler(name, c.Updater(), handler))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationController) OnChange(ctx context.Context, name string, sync MachineRegistrationHandler) {
|
||||||
|
c.AddGenericHandler(ctx, name, FromMachineRegistrationHandlerToHandler(sync))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationController) OnRemove(ctx context.Context, name string, sync MachineRegistrationHandler) {
|
||||||
|
c.AddGenericHandler(ctx, name, generic.NewRemoveHandler(name, c.Updater(), FromMachineRegistrationHandlerToHandler(sync)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationController) Enqueue(namespace, name string) {
|
||||||
|
c.controller.Enqueue(namespace, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationController) EnqueueAfter(namespace, name string, duration time.Duration) {
|
||||||
|
c.controller.EnqueueAfter(namespace, name, duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationController) Informer() cache.SharedIndexInformer {
|
||||||
|
return c.controller.Informer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationController) GroupVersionKind() schema.GroupVersionKind {
|
||||||
|
return c.gvk
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationController) Cache() MachineRegistrationCache {
|
||||||
|
return &machineRegistrationCache{
|
||||||
|
indexer: c.Informer().GetIndexer(),
|
||||||
|
resource: c.groupResource,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationController) Create(obj *v1.MachineRegistration) (*v1.MachineRegistration, error) {
|
||||||
|
result := &v1.MachineRegistration{}
|
||||||
|
return result, c.client.Create(context.TODO(), obj.Namespace, obj, result, metav1.CreateOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationController) Update(obj *v1.MachineRegistration) (*v1.MachineRegistration, error) {
|
||||||
|
result := &v1.MachineRegistration{}
|
||||||
|
return result, c.client.Update(context.TODO(), obj.Namespace, obj, result, metav1.UpdateOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationController) UpdateStatus(obj *v1.MachineRegistration) (*v1.MachineRegistration, error) {
|
||||||
|
result := &v1.MachineRegistration{}
|
||||||
|
return result, c.client.UpdateStatus(context.TODO(), obj.Namespace, obj, result, metav1.UpdateOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationController) Delete(namespace, name string, options *metav1.DeleteOptions) error {
|
||||||
|
if options == nil {
|
||||||
|
options = &metav1.DeleteOptions{}
|
||||||
|
}
|
||||||
|
return c.client.Delete(context.TODO(), namespace, name, *options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationController) Get(namespace, name string, options metav1.GetOptions) (*v1.MachineRegistration, error) {
|
||||||
|
result := &v1.MachineRegistration{}
|
||||||
|
return result, c.client.Get(context.TODO(), namespace, name, result, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationController) List(namespace string, opts metav1.ListOptions) (*v1.MachineRegistrationList, error) {
|
||||||
|
result := &v1.MachineRegistrationList{}
|
||||||
|
return result, c.client.List(context.TODO(), namespace, result, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationController) Watch(namespace string, opts metav1.ListOptions) (watch.Interface, error) {
|
||||||
|
return c.client.Watch(context.TODO(), namespace, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationController) Patch(namespace, name string, pt types.PatchType, data []byte, subresources ...string) (*v1.MachineRegistration, error) {
|
||||||
|
result := &v1.MachineRegistration{}
|
||||||
|
return result, c.client.Patch(context.TODO(), namespace, name, pt, data, result, metav1.PatchOptions{}, subresources...)
|
||||||
|
}
|
||||||
|
|
||||||
|
type machineRegistrationCache struct {
|
||||||
|
indexer cache.Indexer
|
||||||
|
resource schema.GroupResource
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationCache) Get(namespace, name string) (*v1.MachineRegistration, error) {
|
||||||
|
obj, exists, err := c.indexer.GetByKey(namespace + "/" + name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return nil, errors.NewNotFound(c.resource, name)
|
||||||
|
}
|
||||||
|
return obj.(*v1.MachineRegistration), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationCache) List(namespace string, selector labels.Selector) (ret []*v1.MachineRegistration, err error) {
|
||||||
|
|
||||||
|
err = cache.ListAllByNamespace(c.indexer, namespace, selector, func(m interface{}) {
|
||||||
|
ret = append(ret, m.(*v1.MachineRegistration))
|
||||||
|
})
|
||||||
|
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationCache) AddIndexer(indexName string, indexer MachineRegistrationIndexer) {
|
||||||
|
utilruntime.Must(c.indexer.AddIndexers(map[string]cache.IndexFunc{
|
||||||
|
indexName: func(obj interface{}) (strings []string, e error) {
|
||||||
|
return indexer(obj.(*v1.MachineRegistration))
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *machineRegistrationCache) GetByIndex(indexName, key string) (result []*v1.MachineRegistration, err error) {
|
||||||
|
objs, err := c.indexer.ByIndex(indexName, key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = make([]*v1.MachineRegistration, 0, len(objs))
|
||||||
|
for _, obj := range objs {
|
||||||
|
result = append(result, obj.(*v1.MachineRegistration))
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type MachineRegistrationStatusHandler func(obj *v1.MachineRegistration, status v1.MachineRegistrationStatus) (v1.MachineRegistrationStatus, error)
|
||||||
|
|
||||||
|
type MachineRegistrationGeneratingHandler func(obj *v1.MachineRegistration, status v1.MachineRegistrationStatus) ([]runtime.Object, v1.MachineRegistrationStatus, error)
|
||||||
|
|
||||||
|
func RegisterMachineRegistrationStatusHandler(ctx context.Context, controller MachineRegistrationController, condition condition.Cond, name string, handler MachineRegistrationStatusHandler) {
|
||||||
|
statusHandler := &machineRegistrationStatusHandler{
|
||||||
|
client: controller,
|
||||||
|
condition: condition,
|
||||||
|
handler: handler,
|
||||||
|
}
|
||||||
|
controller.AddGenericHandler(ctx, name, FromMachineRegistrationHandlerToHandler(statusHandler.sync))
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterMachineRegistrationGeneratingHandler(ctx context.Context, controller MachineRegistrationController, apply apply.Apply,
|
||||||
|
condition condition.Cond, name string, handler MachineRegistrationGeneratingHandler, opts *generic.GeneratingHandlerOptions) {
|
||||||
|
statusHandler := &machineRegistrationGeneratingHandler{
|
||||||
|
MachineRegistrationGeneratingHandler: handler,
|
||||||
|
apply: apply,
|
||||||
|
name: name,
|
||||||
|
gvk: controller.GroupVersionKind(),
|
||||||
|
}
|
||||||
|
if opts != nil {
|
||||||
|
statusHandler.opts = *opts
|
||||||
|
}
|
||||||
|
controller.OnChange(ctx, name, statusHandler.Remove)
|
||||||
|
RegisterMachineRegistrationStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
type machineRegistrationStatusHandler struct {
|
||||||
|
client MachineRegistrationClient
|
||||||
|
condition condition.Cond
|
||||||
|
handler MachineRegistrationStatusHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *machineRegistrationStatusHandler) sync(key string, obj *v1.MachineRegistration) (*v1.MachineRegistration, error) {
|
||||||
|
if obj == nil {
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
origStatus := obj.Status.DeepCopy()
|
||||||
|
obj = obj.DeepCopy()
|
||||||
|
newStatus, err := a.handler(obj, obj.Status)
|
||||||
|
if err != nil {
|
||||||
|
// Revert to old status on error
|
||||||
|
newStatus = *origStatus.DeepCopy()
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.condition != "" {
|
||||||
|
if errors.IsConflict(err) {
|
||||||
|
a.condition.SetError(&newStatus, "", nil)
|
||||||
|
} else {
|
||||||
|
a.condition.SetError(&newStatus, "", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !equality.Semantic.DeepEqual(origStatus, &newStatus) {
|
||||||
|
if a.condition != "" {
|
||||||
|
// Since status has changed, update the lastUpdatedTime
|
||||||
|
a.condition.LastUpdated(&newStatus, time.Now().UTC().Format(time.RFC3339))
|
||||||
|
}
|
||||||
|
|
||||||
|
var newErr error
|
||||||
|
obj.Status = newStatus
|
||||||
|
newObj, newErr := a.client.UpdateStatus(obj)
|
||||||
|
if err == nil {
|
||||||
|
err = newErr
|
||||||
|
}
|
||||||
|
if newErr == nil {
|
||||||
|
obj = newObj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type machineRegistrationGeneratingHandler struct {
|
||||||
|
MachineRegistrationGeneratingHandler
|
||||||
|
apply apply.Apply
|
||||||
|
opts generic.GeneratingHandlerOptions
|
||||||
|
gvk schema.GroupVersionKind
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *machineRegistrationGeneratingHandler) Remove(key string, obj *v1.MachineRegistration) (*v1.MachineRegistration, error) {
|
||||||
|
if obj != nil {
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = &v1.MachineRegistration{}
|
||||||
|
obj.Namespace, obj.Name = kv.RSplit(key, "/")
|
||||||
|
obj.SetGroupVersionKind(a.gvk)
|
||||||
|
|
||||||
|
return nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
WithOwner(obj).
|
||||||
|
WithSetID(a.name).
|
||||||
|
ApplyObjects()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *machineRegistrationGeneratingHandler) Handle(obj *v1.MachineRegistration, status v1.MachineRegistrationStatus) (v1.MachineRegistrationStatus, error) {
|
||||||
|
if !obj.DeletionTimestamp.IsZero() {
|
||||||
|
return status, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
objs, newStatus, err := a.MachineRegistrationGeneratingHandler(obj, status)
|
||||||
|
if err != nil {
|
||||||
|
return newStatus, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return newStatus, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
WithOwner(obj).
|
||||||
|
WithSetID(a.name).
|
||||||
|
ApplyObjects(objs...)
|
||||||
|
}
|
@@ -11,16 +11,24 @@ import (
|
|||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Run(automatic bool, configFile string) error {
|
func Run(automatic bool, configFile string, powerOff bool, silent bool) error {
|
||||||
cfg, err := config.ReadConfig(configFile)
|
cfg, err := config.ReadConfig(configFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if powerOff {
|
||||||
|
cfg.RancherOS.Install.PowerOff = true
|
||||||
|
}
|
||||||
|
|
||||||
if automatic && !cfg.RancherOS.Install.Automatic {
|
if automatic && !cfg.RancherOS.Install.Automatic {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if silent {
|
||||||
|
cfg.RancherOS.Install.Automatic = true
|
||||||
|
}
|
||||||
|
|
||||||
err = Ask(&cfg)
|
err = Ask(&cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/rancher/os2/pkg/clients"
|
"github.com/rancher/os2/pkg/clients"
|
||||||
"github.com/rancher/os2/pkg/controllers/inventory"
|
"github.com/rancher/os2/pkg/controllers/inventory"
|
||||||
"github.com/rancher/os2/pkg/controllers/managedos"
|
"github.com/rancher/os2/pkg/controllers/managedos"
|
||||||
|
"github.com/rancher/os2/pkg/controllers/registration"
|
||||||
"github.com/rancher/os2/pkg/server"
|
"github.com/rancher/os2/pkg/server"
|
||||||
"github.com/rancher/steve/pkg/aggregation"
|
"github.com/rancher/steve/pkg/aggregation"
|
||||||
"github.com/rancher/wrangler/pkg/crd"
|
"github.com/rancher/wrangler/pkg/crd"
|
||||||
@@ -39,6 +40,10 @@ func Run(ctx context.Context, namespace string) error {
|
|||||||
SchemaObject: v1.MachineInventory{},
|
SchemaObject: v1.MachineInventory{},
|
||||||
Status: true,
|
Status: true,
|
||||||
},
|
},
|
||||||
|
crd.CRD{
|
||||||
|
SchemaObject: v1.MachineRegistration{},
|
||||||
|
Status: true,
|
||||||
|
},
|
||||||
).BatchWait()
|
).BatchWait()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatalf("Failed to create CRDs: %v", err)
|
logrus.Fatalf("Failed to create CRDs: %v", err)
|
||||||
@@ -46,6 +51,7 @@ func Run(ctx context.Context, namespace string) error {
|
|||||||
|
|
||||||
managedos.Register(ctx, clients)
|
managedos.Register(ctx, clients)
|
||||||
inventory.Register(ctx, clients)
|
inventory.Register(ctx, clients)
|
||||||
|
registration.Register(ctx, clients)
|
||||||
|
|
||||||
aggregation.Watch(ctx, clients.Core.Secret(), namespace, "rancheros-operator", server.New(clients))
|
aggregation.Watch(ctx, clients.Core.Secret(), namespace, "rancheros-operator", server.New(clients))
|
||||||
return clients.Start(ctx)
|
return clients.Start(ctx)
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -29,7 +30,7 @@ func (i *InventoryServer) cacerts(rw http.ResponseWriter, req *http.Request) {
|
|||||||
|
|
||||||
if authorization != "" && nonce != "" {
|
if authorization != "" && nonce != "" {
|
||||||
crt, err := i.secretCache.GetByIndex(tokenHash, authorization)
|
crt, err := i.secretCache.GetByIndex(tokenHash, authorization)
|
||||||
if err == nil && len(crt) >= 0 {
|
if err == nil && len(crt) > 0 {
|
||||||
digest := hmac.New(sha512.New, crt[0].Data[tokenKey])
|
digest := hmac.New(sha512.New, crt[0].Data[tokenKey])
|
||||||
digest.Write([]byte(nonce))
|
digest.Write([]byte(nonce))
|
||||||
digest.Write([]byte{0})
|
digest.Write([]byte{0})
|
||||||
@@ -37,6 +38,14 @@ func (i *InventoryServer) cacerts(rw http.ResponseWriter, req *http.Request) {
|
|||||||
digest.Write([]byte{0})
|
digest.Write([]byte{0})
|
||||||
hash := digest.Sum(nil)
|
hash := digest.Sum(nil)
|
||||||
rw.Header().Set("X-Cattle-Hash", base64.StdEncoding.EncodeToString(hash))
|
rw.Header().Set("X-Cattle-Hash", base64.StdEncoding.EncodeToString(hash))
|
||||||
|
} else if machines, err := i.machineCache.GetByIndex(tokenHash, authorization); len(machines) == 1 && err == nil {
|
||||||
|
digest := hmac.New(sha512.New, []byte(machines[0].Spec.TPMHash))
|
||||||
|
digest.Write([]byte(nonce))
|
||||||
|
digest.Write([]byte{0})
|
||||||
|
digest.Write(bytes)
|
||||||
|
digest.Write([]byte{0})
|
||||||
|
hash := digest.Sum(nil)
|
||||||
|
rw.Header().Set("X-Cattle-Hash", base64.StdEncoding.EncodeToString(hash))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,5 +59,22 @@ func (i *InventoryServer) cacert() string {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
if setting.Value == "" {
|
||||||
|
setting, err = i.settingCache.Get("internal-cacerts")
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
return setting.Value
|
return setting.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *InventoryServer) serverURL() (string, error) {
|
||||||
|
setting, err := i.settingCache.Get("server-url")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if setting.Value == "" {
|
||||||
|
return "", fmt.Errorf("server-url is not set")
|
||||||
|
}
|
||||||
|
return setting.Value, nil
|
||||||
|
}
|
||||||
|
182
pkg/server/register.go
Normal file
182
pkg/server/register.go
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/rancher/fleet/pkg/apis/fleet.cattle.io/v1alpha1"
|
||||||
|
v1 "github.com/rancher/os2/pkg/apis/rancheros.cattle.io/v1"
|
||||||
|
values "github.com/rancher/wrangler/pkg/data"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultName = "m-${System Information/Manufacturer}-${System Information/Product Name}-${System Information/Serial Number}-"
|
||||||
|
|
||||||
|
var (
|
||||||
|
sanitize = regexp.MustCompile("[^0-9a-zA-Z]")
|
||||||
|
doubleDash = regexp.MustCompile("--+")
|
||||||
|
start = regexp.MustCompile("^[a-zA-Z]")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (i *InventoryServer) register(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
machineInventory, machineRegister, data, err := i.buildResponse(req)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(resp, err.Error(), http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
machine, writer, err := i.authMachine(resp, req, machineInventory.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(resp, err.Error(), http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if writer == nil {
|
||||||
|
if err := i.sampleConfig(machineRegister, resp); err != nil {
|
||||||
|
http.Error(resp, "authorization required", http.StatusUnauthorized)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer writer.Close()
|
||||||
|
|
||||||
|
if err := i.saveMachine(machine.Spec.TPMHash, machineInventory); err != nil {
|
||||||
|
http.Error(resp, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = writer.Write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *InventoryServer) sampleConfig(machineRegistration *v1.MachineRegistration, writer io.Writer) error {
|
||||||
|
_, err := writer.Write([]byte("#cloud-config\n"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return yaml.NewEncoder(writer).Encode(map[string]interface{}{
|
||||||
|
"rancheros": map[string]interface{}{
|
||||||
|
"install": map[string]interface{}{
|
||||||
|
"registrationURL": machineRegistration.Status.RegistrationURL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *InventoryServer) saveMachine(tpmHash string, machineInventory *v1.MachineInventory) error {
|
||||||
|
machines, err := i.machineCache.GetByIndex(tpmHashIndex, tpmHash)
|
||||||
|
if err != nil || len(machines) > 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
machineInventory.Spec.TPMHash = tpmHash
|
||||||
|
_, err = i.machineClient.Create(machineInventory)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildName(data map[string]interface{}, name string) string {
|
||||||
|
str := name
|
||||||
|
result := &strings.Builder{}
|
||||||
|
for {
|
||||||
|
i := strings.Index(str, "${")
|
||||||
|
if i == -1 {
|
||||||
|
result.WriteString(str)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
j := strings.Index(str[i:], "}")
|
||||||
|
if j == -1 {
|
||||||
|
result.WriteString(str)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
result.WriteString(str[:i])
|
||||||
|
obj := values.GetValueN(data, strings.Split(str[i+2:j+i], "/")...)
|
||||||
|
if str, ok := obj.(string); ok {
|
||||||
|
result.WriteString(str)
|
||||||
|
}
|
||||||
|
str = str[j+i+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
resultStr := sanitize.ReplaceAllString(result.String(), "-")
|
||||||
|
resultStr = doubleDash.ReplaceAllString(resultStr, "-")
|
||||||
|
if !start.MatchString(resultStr) {
|
||||||
|
resultStr = "m" + resultStr
|
||||||
|
}
|
||||||
|
if len(resultStr) > 58 {
|
||||||
|
resultStr = resultStr[:58]
|
||||||
|
}
|
||||||
|
return strings.ToLower(resultStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *InventoryServer) buildResponse(req *http.Request) (*v1.MachineInventory, *v1.MachineRegistration, []byte, error) {
|
||||||
|
token := path.Base(req.URL.Path)
|
||||||
|
|
||||||
|
smbios, err := getSMBios(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
machineRegisters, err := i.machineRegistrationCache.GetByIndex(registrationTokenIndex, token)
|
||||||
|
if apierrors.IsNotFound(err) || len(machineRegisters) != 1 {
|
||||||
|
if len(machineRegisters) > 1 {
|
||||||
|
logrus.Errorf("Multiple MachineRegistrations have the same token %s: %v", token, machineRegisters)
|
||||||
|
}
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
machineRegister := machineRegisters[0]
|
||||||
|
|
||||||
|
name := machineRegister.Spec.MachineName
|
||||||
|
if name == "" {
|
||||||
|
name = defaultName
|
||||||
|
}
|
||||||
|
|
||||||
|
installConfig := map[string]interface{}{}
|
||||||
|
if machineRegister.Spec.CloudConfig != nil && len(machineRegister.Spec.CloudConfig.Data) > 0 {
|
||||||
|
installConfig = values.MergeMapsConcatSlice(installConfig, machineRegister.Spec.CloudConfig.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
serverURL, err := i.serverURL()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
values.PutValue(installConfig, serverURL, "rancherd", "server")
|
||||||
|
values.PutValue(installConfig, "tpm://", "rancherd", "token")
|
||||||
|
values.PutValue(installConfig, true, "rancheros", "install", "automatic")
|
||||||
|
|
||||||
|
data, err := json.Marshal(installConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &v1.MachineInventory{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
GenerateName: buildName(smbios, name),
|
||||||
|
Namespace: machineRegister.Namespace,
|
||||||
|
Labels: machineRegisters[0].Spec.MachineInventoryLabels,
|
||||||
|
Annotations: machineRegisters[0].Spec.MachineInventoryAnnotations,
|
||||||
|
},
|
||||||
|
Spec: v1.MachineInventorySpec{
|
||||||
|
SMBIOS: &v1alpha1.GenericMap{
|
||||||
|
Data: smbios,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, machineRegister, data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSMBios(req *http.Request) (map[string]interface{}, error) {
|
||||||
|
smbios := req.Header.Get("X-Cattle-Smbios")
|
||||||
|
if smbios == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
smbiosData, err := base64.StdEncoding.DecodeString(smbios)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data := map[string]interface{}{}
|
||||||
|
return data, json.Unmarshal(smbiosData, &data)
|
||||||
|
}
|
81
pkg/server/register_test.go
Normal file
81
pkg/server/register_test.go
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gotest.tools/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBuildName(t *testing.T) {
|
||||||
|
data := map[string]interface{}{
|
||||||
|
"level1A": map[string]interface{}{
|
||||||
|
"level2A": "level2AValue",
|
||||||
|
"level2B": map[string]interface{}{
|
||||||
|
"level3A": "level3AValue",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"level1B": "level1BValue",
|
||||||
|
}
|
||||||
|
|
||||||
|
testCase := []struct {
|
||||||
|
Format string
|
||||||
|
Output string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Format: "${level1B}",
|
||||||
|
Output: "level1bvalue",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Format: "${level1B",
|
||||||
|
Output: "m-level1b",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Format: "a${level1B",
|
||||||
|
Output: "a-level1b",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Format: "${}",
|
||||||
|
Output: "m",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Format: "${",
|
||||||
|
Output: "m-",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Format: "a${",
|
||||||
|
Output: "a-",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Format: "${level1A}",
|
||||||
|
Output: "m",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Format: "a${level1A}c",
|
||||||
|
Output: "ac",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Format: "a${level1A}",
|
||||||
|
Output: "a",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Format: "${level1A}c",
|
||||||
|
Output: "c",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Format: "a${level1A/level2A}c",
|
||||||
|
Output: "alevel2avaluec",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Format: "a${level1A/level2B/level3A}c",
|
||||||
|
Output: "alevel3avaluec",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Format: "a${level1A/level2B/level3A}c${level1B}",
|
||||||
|
Output: "alevel3avalueclevel1bvalue",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCase {
|
||||||
|
assert.Equal(t, testCase.Output, buildName(data, testCase.Format))
|
||||||
|
}
|
||||||
|
}
|
86
pkg/server/secret.go
Normal file
86
pkg/server/secret.go
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
v1 "github.com/rancher/os2/pkg/apis/rancheros.cattle.io/v1"
|
||||||
|
"github.com/rancher/os2/pkg/clients"
|
||||||
|
roscontrollers "github.com/rancher/os2/pkg/generated/controllers/rancheros.cattle.io/v1"
|
||||||
|
corecontrollers "github.com/rancher/wrangler/pkg/generated/controllers/core/v1"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sharedSecretAuth struct {
|
||||||
|
secretCache corecontrollers.SecretCache
|
||||||
|
machineCache roscontrollers.MachineInventoryCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSharedSecretAuth(clients *clients.Clients) *sharedSecretAuth {
|
||||||
|
server := &sharedSecretAuth{
|
||||||
|
secretCache: clients.Core.Secret().Cache(),
|
||||||
|
machineCache: clients.OS.MachineInventory().Cache(),
|
||||||
|
}
|
||||||
|
|
||||||
|
server.secretCache.AddIndexer(tokenIndex, func(obj *corev1.Secret) ([]string, error) {
|
||||||
|
if string(obj.Type) != tokenType {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
t := obj.Data[tokenKey]
|
||||||
|
if len(t) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return []string{base64.StdEncoding.EncodeToString(t)}, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
server.machineCache.AddIndexer(machineBySecretNameIndex, func(obj *v1.MachineInventory) ([]string, error) {
|
||||||
|
if obj.Spec.MachineTokenSecretName == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return []string{obj.Namespace + "/" + obj.Spec.MachineTokenSecretName}, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sharedSecretAuth) Authenticate(resp http.ResponseWriter, req *http.Request, registerNamespace string) (*v1.MachineInventory, bool, io.WriteCloser, error) {
|
||||||
|
token := strings.TrimPrefix(req.Header.Get("Authorization"), "Bearer ")
|
||||||
|
if token == "" || registerNamespace != "" {
|
||||||
|
return nil, true, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
secrets, err := s.secretCache.GetByIndex(tokenIndex, token)
|
||||||
|
if apierrors.IsNotFound(err) || len(secrets) == 0 {
|
||||||
|
return nil, false, nil, fmt.Errorf("token not found")
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, false, nil, err
|
||||||
|
} else if len(secrets) > 1 {
|
||||||
|
logrus.Errorf("Multiple machine secrets with the same value [%s/%s, %s/%s, ...]",
|
||||||
|
secrets[0].Namespace, secrets[0].Name, secrets[1].Namespace, secrets[1].Name)
|
||||||
|
return nil, false, nil, fmt.Errorf("token not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
machines, err := s.machineCache.GetByIndex(machineBySecretNameIndex, secrets[0].Namespace+"/"+secrets[0].Name)
|
||||||
|
if len(machines) > 1 {
|
||||||
|
logrus.Errorf("Multiple machine inventories with the token: %v", machines)
|
||||||
|
}
|
||||||
|
if apierrors.IsNotFound(err) || len(machines) != 1 {
|
||||||
|
return nil, false, nil, fmt.Errorf("machine not found")
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, false, nil, err
|
||||||
|
}
|
||||||
|
return machines[0], false, writeCloser{resp}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type writeCloser struct {
|
||||||
|
io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (writeCloser) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
@@ -5,16 +5,18 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
v1 "github.com/rancher/os2/pkg/apis/rancheros.cattle.io/v1"
|
v1 "github.com/rancher/os2/pkg/apis/rancheros.cattle.io/v1"
|
||||||
"github.com/rancher/os2/pkg/clients"
|
"github.com/rancher/os2/pkg/clients"
|
||||||
ranchercontrollers "github.com/rancher/os2/pkg/generated/controllers/management.cattle.io/v3"
|
ranchercontrollers "github.com/rancher/os2/pkg/generated/controllers/management.cattle.io/v3"
|
||||||
roscontrollers "github.com/rancher/os2/pkg/generated/controllers/rancheros.cattle.io/v1"
|
roscontrollers "github.com/rancher/os2/pkg/generated/controllers/rancheros.cattle.io/v1"
|
||||||
|
"github.com/rancher/os2/pkg/tpm"
|
||||||
v3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
|
v3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
|
||||||
corecontrollers "github.com/rancher/wrangler/pkg/generated/controllers/core/v1"
|
corecontrollers "github.com/rancher/wrangler/pkg/generated/controllers/core/v1"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
)
|
)
|
||||||
@@ -24,43 +26,40 @@ var (
|
|||||||
tokenKey = "token"
|
tokenKey = "token"
|
||||||
tokenIndex = "tokenIndex"
|
tokenIndex = "tokenIndex"
|
||||||
machineBySecretNameIndex = "machineBySecretNameIndex"
|
machineBySecretNameIndex = "machineBySecretNameIndex"
|
||||||
|
registrationTokenIndex = "registrationTokenIndex"
|
||||||
|
tpmHashIndex = "tpmHashIndex"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type authenticator interface {
|
||||||
|
Authenticate(resp http.ResponseWriter, req *http.Request, registerNamespace string) (*v1.MachineInventory, bool, io.WriteCloser, error)
|
||||||
|
}
|
||||||
|
|
||||||
type InventoryServer struct {
|
type InventoryServer struct {
|
||||||
secretCache corecontrollers.SecretCache
|
|
||||||
settingCache ranchercontrollers.SettingCache
|
settingCache ranchercontrollers.SettingCache
|
||||||
|
secretCache corecontrollers.SecretCache
|
||||||
machineCache roscontrollers.MachineInventoryCache
|
machineCache roscontrollers.MachineInventoryCache
|
||||||
|
machineClient roscontrollers.MachineInventoryClient
|
||||||
|
machineRegistrationCache roscontrollers.MachineRegistrationCache
|
||||||
clusterRegistrationToken ranchercontrollers.ClusterRegistrationTokenCache
|
clusterRegistrationToken ranchercontrollers.ClusterRegistrationTokenCache
|
||||||
|
authenticators []authenticator
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(clients *clients.Clients) *InventoryServer {
|
func New(clients *clients.Clients) *InventoryServer {
|
||||||
server := &InventoryServer{
|
server := &InventoryServer{
|
||||||
|
authenticators: []authenticator{
|
||||||
|
tpm.New(clients),
|
||||||
|
newSharedSecretAuth(clients),
|
||||||
|
},
|
||||||
secretCache: clients.Core.Secret().Cache(),
|
secretCache: clients.Core.Secret().Cache(),
|
||||||
settingCache: clients.Rancher.Setting().Cache(),
|
|
||||||
machineCache: clients.OS.MachineInventory().Cache(),
|
machineCache: clients.OS.MachineInventory().Cache(),
|
||||||
|
machineClient: clients.OS.MachineInventory(),
|
||||||
|
machineRegistrationCache: clients.OS.MachineRegistration().Cache(),
|
||||||
|
settingCache: clients.Rancher.Setting().Cache(),
|
||||||
clusterRegistrationToken: clients.Rancher.ClusterRegistrationToken().Cache(),
|
clusterRegistrationToken: clients.Rancher.ClusterRegistrationToken().Cache(),
|
||||||
}
|
}
|
||||||
|
|
||||||
server.secretCache.AddIndexer(tokenIndex, func(obj *corev1.Secret) ([]string, error) {
|
|
||||||
if string(obj.Type) != tokenType {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
t := obj.Data[tokenKey]
|
|
||||||
if len(t) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return []string{base64.StdEncoding.EncodeToString(t)}, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
server.machineCache.AddIndexer(machineBySecretNameIndex, func(obj *v1.MachineInventory) ([]string, error) {
|
|
||||||
if obj.Spec.MachineTokenSecretName == "" {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return []string{obj.Namespace + "/" + obj.Spec.MachineTokenSecretName}, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
server.secretCache.AddIndexer(tokenHash, func(obj *corev1.Secret) ([]string, error) {
|
server.secretCache.AddIndexer(tokenHash, func(obj *corev1.Secret) ([]string, error) {
|
||||||
if string(obj.Type) == tokenType {
|
if string(obj.Type) != tokenType {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if token := obj.Data[tokenKey]; len(token) > 0 {
|
if token := obj.Data[tokenKey]; len(token) > 0 {
|
||||||
@@ -70,54 +69,89 @@ func New(clients *clients.Clients) *InventoryServer {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
server.machineCache.AddIndexer(tokenHash, func(obj *v1.MachineInventory) ([]string, error) {
|
||||||
|
if obj.Spec.TPMHash == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
hash := sha256.Sum256([]byte(obj.Spec.TPMHash))
|
||||||
|
return []string{base64.StdEncoding.EncodeToString(hash[:])}, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
server.machineRegistrationCache.AddIndexer(registrationTokenIndex, func(obj *v1.MachineRegistration) ([]string, error) {
|
||||||
|
if obj.Status.RegistrationToken == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return []string{
|
||||||
|
obj.Status.RegistrationToken,
|
||||||
|
}, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
server.machineCache.AddIndexer(tpmHashIndex, func(obj *v1.MachineInventory) ([]string, error) {
|
||||||
|
if obj.Spec.TPMHash == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return []string{obj.Spec.TPMHash}, nil
|
||||||
|
})
|
||||||
|
|
||||||
return server
|
return server
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *InventoryServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
|
func (i *InventoryServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
|
||||||
if strings.HasSuffix(req.URL.Path, "/cacerts") {
|
if strings.Contains(req.URL.Path, "/registration/") {
|
||||||
|
i.register(resp, req)
|
||||||
|
} else if strings.HasSuffix(req.URL.Path, "/cacerts") {
|
||||||
i.cacerts(resp, req)
|
i.cacerts(resp, req)
|
||||||
} else {
|
} else {
|
||||||
err := i.handle(resp, req)
|
i.handle(resp, req)
|
||||||
if err != nil {
|
|
||||||
http.Error(resp, err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *InventoryServer) handle(resp http.ResponseWriter, req *http.Request) error {
|
func (i *InventoryServer) authMachine(resp http.ResponseWriter, req *http.Request, registerNamespace string) (*v1.MachineInventory, io.WriteCloser, error) {
|
||||||
token := strings.TrimPrefix(req.Header.Get("Authorization"), "Bearer ")
|
for _, auth := range i.authenticators {
|
||||||
secrets, err := i.secretCache.GetByIndex(tokenIndex, token)
|
machine, cont, writer, err := auth.Authenticate(resp, req, registerNamespace)
|
||||||
if apierrors.IsNotFound(err) {
|
if err != nil {
|
||||||
http.Error(resp, "Token not found", http.StatusNotFound)
|
return nil, nil, err
|
||||||
return nil
|
|
||||||
} else if err != nil {
|
|
||||||
return err
|
|
||||||
} else if len(secrets) > 1 {
|
|
||||||
logrus.Errorf("Multiple machine secrets with the same value [%s/%s, %s/%s, ...]",
|
|
||||||
secrets[0].Namespace, secrets[0].Name, secrets[1].Namespace, secrets[1].Name)
|
|
||||||
http.Error(resp, "Token not found", http.StatusNotFound)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
if machine != nil || !cont {
|
||||||
|
return machine, writer, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
machines, err := i.machineCache.GetByIndex(machineBySecretNameIndex, secrets[0].Namespace+"/"+secrets[0].Name)
|
func writeErr(writer io.Writer, resp http.ResponseWriter, err error) {
|
||||||
if len(machines) > 1 {
|
message := "Unauthorized"
|
||||||
logrus.Errorf("Multiple machine inventories with the token: %v", machines)
|
if err != nil {
|
||||||
|
message = err.Error()
|
||||||
}
|
}
|
||||||
if apierrors.IsNotFound(err) || len(machines) != 1 {
|
if writer == nil {
|
||||||
http.Error(resp, "Machine not found", http.StatusNotFound)
|
http.Error(resp, message, http.StatusUnauthorized)
|
||||||
return nil
|
} else {
|
||||||
} else if err != nil {
|
writer.Write([]byte(message))
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
crt, err := i.clusterRegistrationToken.Get(machines[0].Status.ClusterRegistrationTokenNamespace,
|
func (i *InventoryServer) handle(resp http.ResponseWriter, req *http.Request) {
|
||||||
machines[0].Status.ClusterRegistrationTokenName)
|
machine, writer, err := i.authMachine(resp, req, "")
|
||||||
|
if machine == nil || err != nil {
|
||||||
|
writeErr(writer, resp, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer writer.Close()
|
||||||
|
|
||||||
|
if machine.Spec.ClusterName == "" {
|
||||||
|
writeErr(writer, resp, errors.New("cluster not assigned"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
crt, err := i.clusterRegistrationToken.Get(machine.Status.ClusterRegistrationTokenNamespace,
|
||||||
|
machine.Status.ClusterRegistrationTokenName)
|
||||||
if apierrors.IsNotFound(err) || crt.Status.Token == "" {
|
if apierrors.IsNotFound(err) || crt.Status.Token == "" {
|
||||||
http.Error(resp, "Cluster token not found", http.StatusNotFound)
|
writeErr(writer, resp, errors.New("cluster token not assigned"))
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return writeResponse(resp, machines[0], crt)
|
if err := writeResponse(writer, machine, crt); err != nil {
|
||||||
|
writeErr(writer, resp, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
@@ -127,11 +161,10 @@ type config struct {
|
|||||||
InternalAddress string `json:"internalAddress,omitempty"`
|
InternalAddress string `json:"internalAddress,omitempty"`
|
||||||
Taints []string `json:"taints,omitempty"`
|
Taints []string `json:"taints,omitempty"`
|
||||||
Labels []string `json:"labels,omitempty"`
|
Labels []string `json:"labels,omitempty"`
|
||||||
ConfigValues map[string]string `json:"extraConfig,omitempty"`
|
|
||||||
Token string `json:"token,omitempty"`
|
Token string `json:"token,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeResponse(resp http.ResponseWriter, inventory *v1.MachineInventory, crt *v3.ClusterRegistrationToken) error {
|
func writeResponse(writer io.Writer, inventory *v1.MachineInventory, crt *v3.ClusterRegistrationToken) error {
|
||||||
config := config{
|
config := config{
|
||||||
Role: inventory.Spec.Config.Role,
|
Role: inventory.Spec.Config.Role,
|
||||||
NodeName: inventory.Spec.Config.NodeName,
|
NodeName: inventory.Spec.Config.NodeName,
|
||||||
@@ -139,7 +172,6 @@ func writeResponse(resp http.ResponseWriter, inventory *v1.MachineInventory, crt
|
|||||||
InternalAddress: inventory.Spec.Config.InternalAddress,
|
InternalAddress: inventory.Spec.Config.InternalAddress,
|
||||||
Taints: nil,
|
Taints: nil,
|
||||||
Labels: nil,
|
Labels: nil,
|
||||||
ConfigValues: inventory.Spec.Config.ConfigValues,
|
|
||||||
Token: crt.Status.Token,
|
Token: crt.Status.Token,
|
||||||
}
|
}
|
||||||
for k, v := range inventory.Spec.Config.Labels {
|
for k, v := range inventory.Spec.Config.Labels {
|
||||||
@@ -148,6 +180,5 @@ func writeResponse(resp http.ResponseWriter, inventory *v1.MachineInventory, crt
|
|||||||
for _, taint := range inventory.Spec.Config.Taints {
|
for _, taint := range inventory.Spec.Config.Taints {
|
||||||
config.Labels = append(config.Labels, taint.ToString())
|
config.Labels = append(config.Labels, taint.ToString())
|
||||||
}
|
}
|
||||||
resp.Header().Set("Content-Type", "application/json")
|
return json.NewEncoder(writer).Encode(config)
|
||||||
return json.NewEncoder(resp).Encode(config)
|
|
||||||
}
|
}
|
||||||
|
214
pkg/tpm/auth_tpm.go
Normal file
214
pkg/tpm/auth_tpm.go
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
package tpm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/certificate-transparency-go/x509"
|
||||||
|
"github.com/google/go-attestation/attest"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
v1 "github.com/rancher/os2/pkg/apis/rancheros.cattle.io/v1"
|
||||||
|
"github.com/rancher/wrangler/pkg/merr"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *AuthServer) verifyChain(ek *attest.EK, namespace string) error {
|
||||||
|
secret, err := a.secretCache.Get(namespace, tpmCACert)
|
||||||
|
if apierrors.IsNotFound(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
roots := x509.NewCertPool()
|
||||||
|
_ = roots.AppendCertsFromPEM(secret.Data[corev1.TLSCertKey])
|
||||||
|
opts := x509.VerifyOptions{
|
||||||
|
Roots: roots,
|
||||||
|
}
|
||||||
|
_, err = ek.Certificate.Verify(opts)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthServer) generateChallenge(ek *attest.EK, attestationData *AttestationData) ([]byte, []byte, error) {
|
||||||
|
ap := attest.ActivationParameters{
|
||||||
|
TPMVersion: attest.TPMVersion20,
|
||||||
|
EK: ek.Public,
|
||||||
|
AK: *attestationData.AK,
|
||||||
|
}
|
||||||
|
|
||||||
|
secret, ec, err := ap.Generate()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("generating challenge: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
challengeBytes, err := json.Marshal(Challenge{EC: ec})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("marshalling challenge: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return secret, challengeBytes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthServer) validateChallenge(secret, resp []byte) error {
|
||||||
|
var response ChallengeResponse
|
||||||
|
if err := json.Unmarshal(resp, &response); err != nil {
|
||||||
|
return fmt.Errorf("unmarshalling challenge response: %w", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(secret, response.Secret) {
|
||||||
|
return fmt.Errorf("invalid challenge response")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthServer) validHash(ek *attest.EK, registerNamespace string) (*v1.MachineInventory, error) {
|
||||||
|
hashEncoded, err := GetPubHash(ek)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("tpm: could not get public key hash: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if registerNamespace != "" {
|
||||||
|
if err := a.verifyChain(ek, registerNamespace); err != nil {
|
||||||
|
return nil, fmt.Errorf("verifying chain: %w", err)
|
||||||
|
}
|
||||||
|
return &v1.MachineInventory{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Namespace: registerNamespace,
|
||||||
|
},
|
||||||
|
Spec: v1.MachineInventorySpec{
|
||||||
|
TPMHash: hashEncoded,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
machines, err := a.machineCache.GetByIndex(machineByHash, hashEncoded)
|
||||||
|
if apierrors.IsNotFound(err) || len(machines) != 1 {
|
||||||
|
if len(machines) > 1 {
|
||||||
|
logrus.Errorf("multiple machines for same hash %s found: %v", hashEncoded, machines)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("failed to find machine")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := a.verifyChain(ek, machines[0].Namespace); err != nil {
|
||||||
|
return nil, fmt.Errorf("verifying chain: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return machines[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeRead(conn *websocket.Conn, input []byte) ([]byte, error) {
|
||||||
|
writer, err := conn.NextWriter(websocket.BinaryMessage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := writer.Write(input); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writer.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, reader, err := conn.NextReader()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ioutil.ReadAll(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
func upgrade(resp http.ResponseWriter, req *http.Request) (*websocket.Conn, error) {
|
||||||
|
upgrader := websocket.Upgrader{
|
||||||
|
HandshakeTimeout: 5 * time.Second,
|
||||||
|
CheckOrigin: func(r *http.Request) bool { return true },
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := upgrader.Upgrade(resp, req, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_ = conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
|
||||||
|
_ = conn.SetReadDeadline(time.Now().Add(10 * time.Second))
|
||||||
|
|
||||||
|
return conn, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthServer) getAttestationData(header string) (*attest.EK, *AttestationData, error) {
|
||||||
|
tpmBytes, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(header, "Bearer TPM"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var attestationData AttestationData
|
||||||
|
if err := json.Unmarshal(tpmBytes, &attestationData); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ek, err := DecodeEK(attestationData.EK)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ek, &attestationData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthServer) Authenticate(resp http.ResponseWriter, req *http.Request, registerNamespace string) (*v1.MachineInventory, bool, io.WriteCloser, error) {
|
||||||
|
header := req.Header.Get("Authorization")
|
||||||
|
if !strings.HasPrefix(header, "Bearer TPM") {
|
||||||
|
return nil, true, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ek, attestationData, err := a.getAttestationData(header)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
machine, err := a.validHash(ek, registerNamespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
secret, challenge, err := a.generateChallenge(ek, attestationData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := upgrade(resp, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
challResp, err := writeRead(conn, challenge)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := a.validateChallenge(secret, challResp); err != nil {
|
||||||
|
return nil, false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
writer, err := conn.NextWriter(websocket.BinaryMessage)
|
||||||
|
return machine, false, &responseWriter{
|
||||||
|
WriteCloser: writer,
|
||||||
|
conn: conn,
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type responseWriter struct {
|
||||||
|
io.WriteCloser
|
||||||
|
conn *websocket.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *responseWriter) Close() error {
|
||||||
|
err := r.WriteCloser.Close()
|
||||||
|
err2 := r.conn.Close()
|
||||||
|
return merr.NewErrors(err, err2)
|
||||||
|
}
|
36
pkg/tpm/tpm.go
Normal file
36
pkg/tpm/tpm.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package tpm
|
||||||
|
|
||||||
|
import (
|
||||||
|
v1 "github.com/rancher/os2/pkg/apis/rancheros.cattle.io/v1"
|
||||||
|
"github.com/rancher/os2/pkg/clients"
|
||||||
|
roscontrollers "github.com/rancher/os2/pkg/generated/controllers/rancheros.cattle.io/v1"
|
||||||
|
corecontrollers "github.com/rancher/wrangler/pkg/generated/controllers/core/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
machineByHash = "machineByHash"
|
||||||
|
tpmCACert = "tpm-ca"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthServer struct {
|
||||||
|
machineCache roscontrollers.MachineInventoryCache
|
||||||
|
machineClient roscontrollers.MachineInventoryClient
|
||||||
|
secretCache corecontrollers.SecretCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(clients *clients.Clients) *AuthServer {
|
||||||
|
a := &AuthServer{
|
||||||
|
machineCache: clients.OS.MachineInventory().Cache(),
|
||||||
|
machineClient: clients.OS.MachineInventory(),
|
||||||
|
secretCache: clients.Core.Secret().Cache(),
|
||||||
|
}
|
||||||
|
|
||||||
|
a.machineCache.AddIndexer(machineByHash, func(obj *v1.MachineInventory) ([]string, error) {
|
||||||
|
if obj.Spec.TPMHash == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return []string{obj.Spec.TPMHash}, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return a
|
||||||
|
}
|
94
pkg/tpm/tpm_attestor.go
Normal file
94
pkg/tpm/tpm_attestor.go
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
** Copyright 2019 Bloomberg Finance L.P.
|
||||||
|
**
|
||||||
|
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
** you may not use this file except in compliance with the License.
|
||||||
|
** You may obtain a copy of the License at
|
||||||
|
**
|
||||||
|
** http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
**
|
||||||
|
** Unless required by applicable law or agreed to in writing, software
|
||||||
|
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
** See the License for the specific language governing permissions and
|
||||||
|
** limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package tpm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/google/certificate-transparency-go/x509"
|
||||||
|
"github.com/google/go-attestation/attest"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AttestationData struct {
|
||||||
|
EK []byte
|
||||||
|
AK *attest.AttestationParameters
|
||||||
|
}
|
||||||
|
|
||||||
|
type Challenge struct {
|
||||||
|
EC *attest.EncryptedCredential
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeyData struct {
|
||||||
|
Keys []string `json:"keys"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChallengeResponse struct {
|
||||||
|
Secret []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPubHash(ek *attest.EK) (string, error) {
|
||||||
|
data, err := pubBytes(ek)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
pubHash := sha256.Sum256(data)
|
||||||
|
hashEncoded := fmt.Sprintf("%x", pubHash)
|
||||||
|
return hashEncoded, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func pubBytes(ek *attest.EK) ([]byte, error) {
|
||||||
|
data, err := x509.MarshalPKIXPublicKey(ek.Public)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error marshaling ec public key: %v", err)
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodeEK(pemBytes []byte) (*attest.EK, error) {
|
||||||
|
block, _ := pem.Decode(pemBytes)
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
return nil, errors.New("invalid pemBytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch block.Type {
|
||||||
|
case "CERTIFICATE":
|
||||||
|
cert, err := x509.ParseCertificate(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error parsing certificate: %v", err)
|
||||||
|
}
|
||||||
|
return &attest.EK{
|
||||||
|
Certificate: cert,
|
||||||
|
Public: cert.PublicKey,
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
case "PUBLIC KEY":
|
||||||
|
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error parsing ecdsa public key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &attest.EK{
|
||||||
|
Public: pub,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("invalid pem type: %s", block.Type)
|
||||||
|
}
|
@@ -27,7 +27,7 @@ RUN cd /usr/sbin && \
|
|||||||
rm tmp
|
rm tmp
|
||||||
RUN cd /usr/src && \
|
RUN cd /usr/src && \
|
||||||
git clone https://github.com/rancher-sandbox/cOS-toolkit
|
git clone https://github.com/rancher-sandbox/cOS-toolkit
|
||||||
RUN curl -Lo /usr/bin/luet https://github.com/mudler/luet/releases/download/0.18.1/luet-0.18.1-linux-$(go env GOARCH) && \
|
RUN curl -Lo /usr/bin/luet https://github.com/mudler/luet/releases/download/0.20.5/luet-0.20.5-linux-$(go env GOARCH) && \
|
||||||
chmod +x /usr/bin/luet
|
chmod +x /usr/bin/luet
|
||||||
RUN mkdir -p /iso/iso-overlay/boot/grub2 /etc/luet
|
RUN mkdir -p /iso/iso-overlay/boot/grub2 /etc/luet
|
||||||
RUN export SUFFIX; \
|
RUN export SUFFIX; \
|
||||||
@@ -104,7 +104,7 @@ RUN echo -e \
|
|||||||
'fi\n'\
|
'fi\n'\
|
||||||
'menuentry "RancherOS Install" --class os --unrestricted {\n'\
|
'menuentry "RancherOS Install" --class os --unrestricted {\n'\
|
||||||
' echo Loading kernel...\n'\
|
' echo Loading kernel...\n'\
|
||||||
' $linux ($root)/boot/kernel.xz cdroot root=live:CDLABEL=COS_LIVE rd.live.dir=/ rd.live.squashimg=rootfs.squashfs console=tty1 console=ttyS0 rd.cos.disable\n'\
|
' $linux ($root)/boot/kernel.xz cdroot root=live:CDLABEL=COS_LIVE rd.live.dir=/ rd.live.squashimg=rootfs.squashfs console=tty1 console=ttyS0 rd.cos.disable rancheros.install.automatic=true rancheros.install.config_url=/run/initramfs/live/config\n'\
|
||||||
' echo Loading initrd...\n'\
|
' echo Loading initrd...\n'\
|
||||||
' $initrd ($root)/boot/rootfs.xz\n'\
|
' $initrd ($root)/boot/rootfs.xz\n'\
|
||||||
'}\n'\
|
'}\n'\
|
||||||
@@ -115,7 +115,13 @@ RUN echo -e \
|
|||||||
' terminal_output console\n'\
|
' terminal_output console\n'\
|
||||||
' }\n'\
|
' }\n'\
|
||||||
'fi\n' > /iso/iso-overlay/boot/grub2/grub.cfg
|
'fi\n' > /iso/iso-overlay/boot/grub2/grub.cfg
|
||||||
|
RUN echo -e '#cloud-config\n'\
|
||||||
|
'rancheros:\n'\
|
||||||
|
' install:\n'\
|
||||||
|
' automatic: false\n' > /iso/iso-overlay/config
|
||||||
RUN luet install --no-spinner -y toolchain/luet-makeiso
|
RUN luet install --no-spinner -y toolchain/luet-makeiso
|
||||||
|
ARG CONFIG
|
||||||
|
RUN if [ -n "$CONFIG" ]; then echo "$CONFIG" > /iso/iso-overlay/config; fi
|
||||||
WORKDIR /usr/src/cOS-toolkit/packer
|
WORKDIR /usr/src/cOS-toolkit/packer
|
||||||
|
|
||||||
FROM tools AS iso-build
|
FROM tools AS iso-build
|
||||||
@@ -179,7 +185,10 @@ EOF
|
|||||||
|
|
||||||
iso()
|
iso()
|
||||||
{
|
{
|
||||||
build --target iso -o build/
|
if [ -n "$CONFIG" ]; then
|
||||||
|
CONFIG_DATA="$(<$CONFIG)"
|
||||||
|
fi
|
||||||
|
build --target iso -o build/ --build-arg CONFIG="${CONFIG_DATA}"
|
||||||
}
|
}
|
||||||
|
|
||||||
qcow()
|
qcow()
|
||||||
@@ -221,14 +230,16 @@ ami()
|
|||||||
usage()
|
usage()
|
||||||
{
|
{
|
||||||
echo "Usage:"
|
echo "Usage:"
|
||||||
echo " $0 IMAGE OUTPUT"
|
echo " $0 IMAGE OUTPUT [ISO_CLOUD_CONFIG]"
|
||||||
echo
|
echo
|
||||||
echo " IMAGE: a Docker image"
|
echo " IMAGE: a Docker image"
|
||||||
echo " OUTPUT: Comma seperated value of output image formats. Valid: aws,iso,qcow"
|
echo " OUTPUT: Comma seperated value of output image formats. Valid: aws,iso,qcow"
|
||||||
|
echo " ISO_CLOUD_CONFIG: An option file that will be used as the default cloud-init in an ISO"
|
||||||
}
|
}
|
||||||
|
|
||||||
IMAGE=$1
|
IMAGE=$1
|
||||||
OUTPUT=$2
|
OUTPUT=$2
|
||||||
|
CONFIG=$3
|
||||||
VERSION=${IMAGE##*:}
|
VERSION=${IMAGE##*:}
|
||||||
NAME=${IMAGE%%:${VERSION}}
|
NAME=${IMAGE%%:${VERSION}}
|
||||||
NAME=${NAME//[^a-zA-Z0-9-@.\/_]/-}
|
NAME=${NAME//[^a-zA-Z0-9-@.\/_]/-}
|
||||||
@@ -258,6 +269,8 @@ fi
|
|||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo Unknown format $i
|
echo Unknown format $i
|
||||||
|
echo
|
||||||
|
usage
|
||||||
exit 1
|
exit 1
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
3
scripts/install-chart
Executable file
3
scripts/install-chart
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
helm -n cattle-rancheros-operator-system upgrade --install --create-namespace rancheros-operator ./chart
|
13
scripts/qemu-in-container
Executable file
13
scripts/qemu-in-container
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ "$#" = 0 ]; then
|
||||||
|
exec bash
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -e /dev/tpm0 ]; then
|
||||||
|
mkdir /tmp/emulated_tpm
|
||||||
|
swtpm socket --tpmstate dir=/tmp/emulated_tpm --ctrl type=unixio,path=/tmp/emulated_tpm/swtpm-sock --log level=1 --tpm2 &
|
||||||
|
|
||||||
|
exec "$@" -chardev socket,id=chrtpm,path=/tmp/emulated_tpm/swtpm-sock \
|
||||||
|
-tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0
|
||||||
|
fi
|
6
scripts/qemu-wrapper
Executable file
6
scripts/qemu-wrapper
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -x -e
|
||||||
|
|
||||||
|
BASE=$(dirname $0)/..
|
||||||
|
docker build -t ros-qemu -f ${BASE}/Dockerfile.kvm ${BASE}
|
||||||
|
exec docker run -it --rm --net=host -v $(dirname $(pwd)):$(dirname $(pwd)) -w $(pwd) --privileged ros-qemu "$@"
|
@@ -38,7 +38,8 @@ if [ "$1" == "" ] && [ ! -e output.iso ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
#-bios /usr/share/qemu/OVMF.fd \
|
#-bios /usr/share/qemu/OVMF.fd \
|
||||||
qemu-system-x86_64 \
|
|
||||||
|
../scripts/qemu-wrapper qemu-system-x86_64 \
|
||||||
$BOOT \
|
$BOOT \
|
||||||
-enable-kvm \
|
-enable-kvm \
|
||||||
-m ${MEMORY:=4096} \
|
-m ${MEMORY:=4096} \
|
||||||
|
Reference in New Issue
Block a user