mirror of https://github.com/k3s-io/k3s
Add iSCSI CHAP authentication
Signed-off-by: Huamin Chen <hchen@redhat.com>pull/6/head
parent
777eb32e91
commit
9298217126
|
@ -88,6 +88,10 @@ func VisitPodSecretNames(pod *api.Pod, visitor func(string) bool) bool {
|
|||
if source.ScaleIO.SecretRef != nil && !visitor(source.ScaleIO.SecretRef.Name) {
|
||||
return false
|
||||
}
|
||||
case source.ISCSI != nil:
|
||||
if source.ISCSI.SecretRef != nil && !visitor(source.ISCSI.SecretRef.Name) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
|
|
@ -85,7 +85,11 @@ func TestPodSecrets(t *testing.T) {
|
|||
VolumeSource: api.VolumeSource{
|
||||
ScaleIO: &api.ScaleIOVolumeSource{
|
||||
SecretRef: &api.LocalObjectReference{
|
||||
Name: "Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef"}}}}},
|
||||
Name: "Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef"}}}}, {
|
||||
VolumeSource: api.VolumeSource{
|
||||
ISCSI: &api.ISCSIVolumeSource{
|
||||
SecretRef: &api.LocalObjectReference{
|
||||
Name: "Spec.Volumes[*].VolumeSource.ISCSI.SecretRef"}}}}},
|
||||
},
|
||||
}
|
||||
extractedNames := sets.NewString()
|
||||
|
@ -114,6 +118,7 @@ func TestPodSecrets(t *testing.T) {
|
|||
"Spec.Volumes[*].VolumeSource.Secret",
|
||||
"Spec.Volumes[*].VolumeSource.Secret.SecretName",
|
||||
"Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef",
|
||||
"Spec.Volumes[*].VolumeSource.ISCSI.SecretRef",
|
||||
)
|
||||
secretPaths := collectSecretPaths(t, nil, "", reflect.TypeOf(&api.Pod{}))
|
||||
secretPaths = secretPaths.Difference(excludedSecretPaths)
|
||||
|
|
|
@ -176,7 +176,10 @@ func VisitPodSecretNames(pod *v1.Pod, visitor func(string) bool) bool {
|
|||
if source.ScaleIO.SecretRef != nil && !visitor(source.ScaleIO.SecretRef.Name) {
|
||||
return false
|
||||
}
|
||||
|
||||
case source.ISCSI != nil:
|
||||
if source.ISCSI.SecretRef != nil && !visitor(source.ISCSI.SecretRef.Name) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
|
|
@ -253,7 +253,11 @@ func TestPodSecrets(t *testing.T) {
|
|||
VolumeSource: v1.VolumeSource{
|
||||
ScaleIO: &v1.ScaleIOVolumeSource{
|
||||
SecretRef: &v1.LocalObjectReference{
|
||||
Name: "Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef"}}}}},
|
||||
Name: "Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef"}}}}, {
|
||||
VolumeSource: v1.VolumeSource{
|
||||
ISCSI: &v1.ISCSIVolumeSource{
|
||||
SecretRef: &v1.LocalObjectReference{
|
||||
Name: "Spec.Volumes[*].VolumeSource.ISCSI.SecretRef"}}}}},
|
||||
},
|
||||
}
|
||||
extractedNames := sets.NewString()
|
||||
|
@ -282,6 +286,7 @@ func TestPodSecrets(t *testing.T) {
|
|||
"Spec.Volumes[*].VolumeSource.Secret",
|
||||
"Spec.Volumes[*].VolumeSource.Secret.SecretName",
|
||||
"Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef",
|
||||
"Spec.Volumes[*].VolumeSource.ISCSI.SecretRef",
|
||||
)
|
||||
secretPaths := collectSecretPaths(t, nil, "", reflect.TypeOf(&v1.Pod{}))
|
||||
secretPaths = secretPaths.Difference(excludedSecretPaths)
|
||||
|
|
|
@ -99,10 +99,23 @@ func (plugin *iscsiPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
|
|||
|
||||
func (plugin *iscsiPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
|
||||
// Inject real implementations here, test through the internal function.
|
||||
return plugin.newMounterInternal(spec, pod.UID, &ISCSIUtil{}, plugin.host.GetMounter())
|
||||
var secret map[string]string
|
||||
source, _, err := getVolumeSource(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if source.SecretRef != nil {
|
||||
if secret, err = ioutil.GetSecretForPod(pod, source.SecretRef.Name, plugin.host.GetKubeClient()); err != nil {
|
||||
glog.Errorf("Couldn't get secret from %v/%v", pod.Namespace, source.SecretRef)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return plugin.newMounterInternal(spec, pod.UID, &ISCSIUtil{}, plugin.host.GetMounter(), secret)
|
||||
}
|
||||
|
||||
func (plugin *iscsiPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager diskManager, mounter mount.Interface) (volume.Mounter, error) {
|
||||
func (plugin *iscsiPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager diskManager, mounter mount.Interface, secret map[string]string) (volume.Mounter, error) {
|
||||
// iscsi volumes used directly in a pod have a ReadOnly flag set by the pod author.
|
||||
// iscsi volumes used as a PersistentVolume gets the ReadOnly flag indirectly through the persistent-claim volume used to mount the PV
|
||||
iscsi, readOnly, err := getVolumeSource(spec)
|
||||
|
@ -121,14 +134,17 @@ func (plugin *iscsiPlugin) newMounterInternal(spec *volume.Spec, podUID types.UI
|
|||
|
||||
return &iscsiDiskMounter{
|
||||
iscsiDisk: &iscsiDisk{
|
||||
podUID: podUID,
|
||||
volName: spec.Name(),
|
||||
portals: bkportal,
|
||||
iqn: iscsi.IQN,
|
||||
lun: lun,
|
||||
iface: iface,
|
||||
manager: manager,
|
||||
plugin: plugin},
|
||||
podUID: podUID,
|
||||
volName: spec.Name(),
|
||||
portals: bkportal,
|
||||
iqn: iscsi.IQN,
|
||||
lun: lun,
|
||||
iface: iface,
|
||||
chap_discovery: iscsi.DiscoveryCHAPAuth,
|
||||
chap_session: iscsi.SessionCHAPAuth,
|
||||
secret: secret,
|
||||
manager: manager,
|
||||
plugin: plugin},
|
||||
fsType: iscsi.FSType,
|
||||
readOnly: readOnly,
|
||||
mounter: &mount.SafeFormatAndMount{Interface: mounter, Runner: exec.New()},
|
||||
|
@ -173,13 +189,16 @@ func (plugin *iscsiPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*v
|
|||
}
|
||||
|
||||
type iscsiDisk struct {
|
||||
volName string
|
||||
podUID types.UID
|
||||
portals []string
|
||||
iqn string
|
||||
lun string
|
||||
iface string
|
||||
plugin *iscsiPlugin
|
||||
volName string
|
||||
podUID types.UID
|
||||
portals []string
|
||||
iqn string
|
||||
lun string
|
||||
iface string
|
||||
chap_discovery bool
|
||||
chap_session bool
|
||||
secret map[string]string
|
||||
plugin *iscsiPlugin
|
||||
// Utility interface that provides API calls to the provider to attach/detach disks.
|
||||
manager diskManager
|
||||
volume.MetricsNil
|
||||
|
|
|
@ -141,7 +141,7 @@ func doTestPlugin(t *testing.T, spec *volume.Spec) {
|
|||
fakeManager := NewFakeDiskManager()
|
||||
defer fakeManager.Cleanup()
|
||||
fakeMounter := &mount.FakeMounter{}
|
||||
mounter, err := plug.(*iscsiPlugin).newMounterInternal(spec, types.UID("poduid"), fakeManager, fakeMounter)
|
||||
mounter, err := plug.(*iscsiPlugin).newMounterInternal(spec, types.UID("poduid"), fakeManager, fakeMounter, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to make a new Mounter: %v", err)
|
||||
}
|
||||
|
|
|
@ -31,6 +31,59 @@ import (
|
|||
"k8s.io/kubernetes/pkg/volume"
|
||||
)
|
||||
|
||||
var (
|
||||
chap_st = []string{
|
||||
"discovery.sendtargets.auth.username",
|
||||
"discovery.sendtargets.auth.password",
|
||||
"discovery.sendtargets.auth.username_in",
|
||||
"discovery.sendtargets.auth.password_in"}
|
||||
chap_sess = []string{
|
||||
"node.session.auth.username",
|
||||
"node.session.auth.password",
|
||||
"node.session.auth.username_in",
|
||||
"node.session.auth.password_in"}
|
||||
)
|
||||
|
||||
func updateISCSIDiscoverydb(b iscsiDiskMounter, tp string) error {
|
||||
if b.chap_discovery {
|
||||
out, err := b.plugin.execCommand("iscsiadm", []string{"-m", "discoverydb", "-t", "sendtargets", "-p", tp, "-I", b.iface, "-o", "update", "-n", "discovery.sendtargets.auth.authmethod", "-v", "CHAP"})
|
||||
if err != nil {
|
||||
return fmt.Errorf("iscsi: failed to update discoverydb with CHAP, output: %v", string(out))
|
||||
}
|
||||
|
||||
for _, k := range chap_st {
|
||||
v := b.secret[k]
|
||||
if len(v) > 0 {
|
||||
out, err := b.plugin.execCommand("iscsiadm", []string{"-m", "discoverydb", "-t", "sendtargets", "-p", tp, "-I", b.iface, "-o", "update", "-n", k, "-v", v})
|
||||
if err != nil {
|
||||
return fmt.Errorf("iscsi: failed to update discoverydb key %q with value %q error: %v", k, v, string(out))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateISCSINode(b iscsiDiskMounter, tp string) error {
|
||||
if b.chap_session {
|
||||
out, err := b.plugin.execCommand("iscsiadm", []string{"-m", "node", "-p", tp, "-T", b.iqn, "-I", b.iface, "-o", "update", "-n", "node.session.auth.authmethod", "-v", "CHAP"})
|
||||
if err != nil {
|
||||
return fmt.Errorf("iscsi: failed to update node with CHAP, output: %v", string(out))
|
||||
}
|
||||
|
||||
for _, k := range chap_sess {
|
||||
v := b.secret[k]
|
||||
if len(v) > 0 {
|
||||
out, err := b.plugin.execCommand("iscsiadm", []string{"-m", "node", "-p", tp, "-T", b.iqn, "-I", b.iface, "-o", "update", "-n", k, "-v", v})
|
||||
if err != nil {
|
||||
return fmt.Errorf("iscsi: failed to update node session key %q with value %q error: %v", k, v, string(out))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// stat a path, if not exists, retry maxRetries times
|
||||
// when iscsi transports other than default are used, use glob instead as pci id of device is unknown
|
||||
type StatFunc func(string) (os.FileInfo, error)
|
||||
|
@ -105,6 +158,7 @@ func (util *ISCSIUtil) AttachDisk(b iscsiDiskMounter) error {
|
|||
var devicePath string
|
||||
var devicePaths []string
|
||||
var iscsiTransport string
|
||||
var lastErr error
|
||||
|
||||
out, err := b.plugin.execCommand("iscsiadm", []string{"-m", "iface", "-I", b.iface, "-o", "show"})
|
||||
if err != nil {
|
||||
|
@ -133,21 +187,41 @@ func (util *ISCSIUtil) AttachDisk(b iscsiDiskMounter) error {
|
|||
}
|
||||
exist := waitForPathToExist(devicePath, 1, iscsiTransport)
|
||||
if exist == false {
|
||||
// discover iscsi target
|
||||
out, err := b.plugin.execCommand("iscsiadm", []string{"-m", "discovery", "-t", "sendtargets", "-p", tp, "-I", b.iface})
|
||||
// build discoverydb and discover iscsi target
|
||||
b.plugin.execCommand("iscsiadm", []string{"-m", "discoverydb", "-t", "sendtargets", "-p", tp, "-I", b.iface, "-o", "new"})
|
||||
// update discoverydb with CHAP secret
|
||||
err = updateISCSIDiscoverydb(b, tp)
|
||||
if err != nil {
|
||||
glog.Errorf("iscsi: failed to sendtargets to portal %s error: %s", tp, string(out))
|
||||
lastErr = fmt.Errorf("iscsi: failed to update discoverydb to portal %s error: %v", tp, err)
|
||||
continue
|
||||
}
|
||||
out, err := b.plugin.execCommand("iscsiadm", []string{"-m", "discoverydb", "-t", "sendtargets", "-p", tp, "-I", b.iface, "--discover"})
|
||||
if err != nil {
|
||||
// delete discoverydb record
|
||||
b.plugin.execCommand("iscsiadm", []string{"-m", "discoverydb", "-t", "sendtargets", "-p", tp, "-I", b.iface, "-o", "delete"})
|
||||
lastErr = fmt.Errorf("iscsi: failed to sendtargets to portal %s output: %s, err %v", tp, string(out), err)
|
||||
continue
|
||||
}
|
||||
err = updateISCSINode(b, tp)
|
||||
if err != nil {
|
||||
// failure to update node db is rare. But deleting record will likely impact those who already start using it.
|
||||
lastErr = fmt.Errorf("iscsi: failed to update iscsi node to portal %s error: %v", tp, err)
|
||||
continue
|
||||
}
|
||||
// login to iscsi target
|
||||
out, err = b.plugin.execCommand("iscsiadm", []string{"-m", "node", "-p", tp, "-T", b.iqn, "-I", b.iface, "--login"})
|
||||
if err != nil {
|
||||
glog.Errorf("iscsi: failed to attach disk:Error: %s (%v)", string(out), err)
|
||||
// delete the node record from database
|
||||
b.plugin.execCommand("iscsiadm", []string{"-m", "node", "-p", tp, "-I", b.iface, "-T", b.iqn, "-o", "delete"})
|
||||
lastErr = fmt.Errorf("iscsi: failed to attach disk: Error: %s (%v)", string(out), err)
|
||||
continue
|
||||
}
|
||||
exist = waitForPathToExist(devicePath, 10, iscsiTransport)
|
||||
if !exist {
|
||||
glog.Errorf("Could not attach disk: Timeout after 10s")
|
||||
// update last error
|
||||
lastErr = fmt.Errorf("Could not attach disk: Timeout after 10s")
|
||||
continue
|
||||
} else {
|
||||
devicePaths = append(devicePaths, devicePath)
|
||||
}
|
||||
|
@ -158,8 +232,8 @@ func (util *ISCSIUtil) AttachDisk(b iscsiDiskMounter) error {
|
|||
}
|
||||
|
||||
if len(devicePaths) == 0 {
|
||||
glog.Errorf("iscsi: failed to get any path for iscsi disk")
|
||||
return errors.New("failed to get any path for iscsi disk")
|
||||
glog.Errorf("iscsi: failed to get any path for iscsi disk, last err seen:\n%v", lastErr)
|
||||
return fmt.Errorf("failed to get any path for iscsi disk, last err seen:\n%v", lastErr)
|
||||
}
|
||||
|
||||
//Make sure we use a valid devicepath to find mpio device.
|
||||
|
@ -233,12 +307,24 @@ func (util *ISCSIUtil) DetachDisk(c iscsiDiskUnmounter, mntPath string) error {
|
|||
if err != nil {
|
||||
glog.Errorf("iscsi: failed to detach disk Error: %s", string(out))
|
||||
}
|
||||
// Delete the node record
|
||||
glog.Infof("iscsi: delete node record target %s iqn %s", portal, iqn)
|
||||
out, err = c.plugin.execCommand("iscsiadm", []string{"-m", "node", "-p", portal, "-T", iqn, "-I", iface, "-o", "delete"})
|
||||
if err != nil {
|
||||
glog.Errorf("iscsi: failed to delete node record Error: %s", string(out))
|
||||
}
|
||||
} else {
|
||||
glog.Infof("iscsi: log out target %s iqn %s", portal, iqn)
|
||||
out, err := c.plugin.execCommand("iscsiadm", []string{"-m", "node", "-p", portal, "-T", iqn, "--logout"})
|
||||
if err != nil {
|
||||
glog.Errorf("iscsi: failed to detach disk Error: %s", string(out))
|
||||
}
|
||||
// Delete the node record
|
||||
glog.Infof("iscsi: delete node record target %s iqn %s", portal, iqn)
|
||||
out, err = c.plugin.execCommand("iscsiadm", []string{"-m", "node", "-p", portal, "-T", iqn, "-o", "delete"})
|
||||
if err != nil {
|
||||
glog.Errorf("iscsi: failed to delete node record Error: %s", string(out))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue