Skip to content
This repository has been archived by the owner on Jul 16, 2021. It is now read-only.

Commit

Permalink
Add authentication options
Browse files Browse the repository at this point in the history
- token
- service account
  • Loading branch information
loewenstein-sap committed Mar 10, 2017
1 parent 995f5d3 commit 70d435a
Show file tree
Hide file tree
Showing 8 changed files with 315 additions and 84 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ $ bosh -e $(minikube ip):31555 \
- Only *dynamic networks* are supported, hence releases that require static IPs are out for now.
- `attach_disk` is implemented by deleting the Pod and creating a new one with the required *persistent disk* attached.
- bosh agent requires to run in privileged containers
- communication with Kubernetes is always insecure (i.e. no SSL verification)

Copyright and license
---------------------
Expand Down
26 changes: 18 additions & 8 deletions jobs/kubernetes_cpi/spec
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,24 @@ packages:
- bosh_kubernetes_cpi

properties:
kubernetes.apiEndpoint:
description: URL of the Kubernetes API endpoint to connect to (required)
kubernetes.namespace:
description: Kubernetes namespace (required)
kubernetes.credentials.certificate:
description: Kubernetes Client certificate (required)
kubernetes.credentials.privateKey:
description: Kubernetes Client private key (required)
kubernetes.cluster-access:
description: Configuration of how to access the Kubernetes API.
If you leave out cluster-access the cpi expects a service account to be available
on the Pod.
default: ServiceAccount
example:
cluster-access: ServiceAccount
cluster-access:
server: https://my.kubernetes.api/
namespace: default
credentials:
token: xxxxx-xxxxx-xxxxx
cluster-access:
server: https://my.kubernetes.api/
namespace: default
credentials:
certificate: my-cert
private-key: my-key

ntp:
description: List of NTP servers
Expand Down
7 changes: 1 addition & 6 deletions jobs/kubernetes_cpi/templates/cpi.json.erb
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
<%=
params = {
'apiEndpoint' => p('kubernetes.apiEndpoint'),
'namespace' => p('kubernetes.namespace'),
'credentials' => {
'certificate' => p('kubernetes.credentials.certificate'),
'privateKey' => p('kubernetes.credentials.privateKey')
},
'cluster-access' => p('kubernetes.cluster-access'),
'agent' => {
'ntp' => p('ntp'),
'mbus' => p('agent.mbus')
Expand Down
7 changes: 7 additions & 0 deletions src/bosh-kubernetes-cpi/bosh-kubernetes-cpi-release.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,20 @@ test-suite bosh-kubernetes-cpi-release-test
hs-source-dirs: test
main-is: Spec.hs
other-modules: CPI.Kubernetes.VmTypesSpec
, CPI.Kubernetes.ConfigSpec
build-depends: base
, hspec
, aeson-qq
, raw-strings-qq
, safe-exceptions
, haskell-bosh-cpi
, bosh-kubernetes-cpi-release
, servant-client
, bytestring
, aeson
, text
, mtl
, unordered-containers
ghc-options: -threaded -rtsopts -with-rtsopts=-N
default-language: Haskell2010

Expand Down
2 changes: 1 addition & 1 deletion src/bosh-kubernetes-cpi/dependencies/bosh-cpi-haskell
117 changes: 86 additions & 31 deletions src/bosh-kubernetes-cpi/src/CPI/Kubernetes/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,87 @@
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}

module CPI.Kubernetes.Config(
Config(..)
, parseConfig
, ClusterAccess(..)
, Credentials(..)
) where

import Prelude hiding (readFile)

import qualified CPI.Base as Base

import Control.Applicative
import Control.Monad.Catch
import CPI.Base.System
import Data.Aeson.Types
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Text.Encoding (encodeUtf8)
import Data.Text.Encoding (decodeUtf8, encodeUtf8)
import Data.Yaml
import GHC.Generics
import Network.TLS
import Network.TLS (Credential,
credentialLoadX509FromMemory)
import qualified Servant.Common.BaseUrl as Url

parseConfig :: ByteString -> IO Config
parseConfig input = do
rawConfig <- readConfig input
apiEndpoint <- Url.parseBaseUrl $ Text.unpack $ _apiEndpoint rawConfig
parseCredentials :: (MonadThrow m) => RawCredentials -> m Credentials
parseCredentials (RawClientCertificate rawCertificate rawPrivateKey) = do
creds <- readCredential
(encodeUtf8 (_certificate . _credentials $ rawConfig))
(encodeUtf8 (_privateKey . _credentials $ rawConfig))
return Config {
apiEndpoint = apiEndpoint,
namespace = _namespace rawConfig,
credentials = creds,
agent = _agent rawConfig
(encodeUtf8 rawCertificate)
(encodeUtf8 rawPrivateKey)
pure $ ClientCertificate creds
parseCredentials (RawToken rawToken) =
pure $ Token rawToken

parseConfig :: (MonadThrow m) => ByteString -> m Config
parseConfig input = do
RawConfig clusterAccess' agent <- readConfig input
clusterAccess <- parseClusterAccess clusterAccess'
pure Config {
clusterAccess = clusterAccess
, agent = agent
}

parseClusterAccess :: (MonadThrow m) => RawClusterAccess -> m ClusterAccess
parseClusterAccess RawServiceAccount = do
server <- Url.parseBaseUrl "https://kubernetes"
pure ClusterAccess {
server = pure server
, namespace = decodeUtf8 <$> readFile "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
, credentials = Token . decodeUtf8 <$> readFile "/var/run/secrets/kubernetes.io/serviceaccount/token"
}
parseClusterAccess RawClusterAccess {
_server = server
, _namespace = namespace
, _credentials = credentials
} = do
server <- Url.parseBaseUrl $ Text.unpack server
credentials <- parseCredentials credentials
pure ClusterAccess {
server = pure server
, namespace = pure namespace
, credentials = pure credentials
}

data Config = Config {
apiEndpoint :: Url.BaseUrl,
namespace :: Text,
credentials :: Credential,
agent :: Object
clusterAccess :: ClusterAccess
, agent :: Object
}

data ClusterAccess = ClusterAccess {
server :: forall m. (FileSystem m) => m Url.BaseUrl
, credentials :: forall m. (FileSystem m) => m Credentials
, namespace :: forall m. (FileSystem m) => m Text
}

data Credentials = ClientCertificate Credential
| Token Text


readConfig :: (MonadThrow m, FromJSON c) => BS.ByteString -> m c
readConfig input = either throwM return $ decodeEither' input
Expand All @@ -51,30 +91,45 @@ readCredential :: (MonadThrow m) => BS.ByteString -> BS.ByteString -> m Credenti
readCredential cert key = either (throwM . Base.ConfigParseException) return $ credentialLoadX509FromMemory cert key

data RawConfig = RawConfig {
_apiEndpoint :: Text,
_namespace :: Text,
_credentials :: RawCredentials,
_agent :: Object
_clusterAccess :: RawClusterAccess
, _agent :: Object
} deriving (Show, Generic)

instance FromJSON RawConfig where
parseJSON (Object v) = RawConfig <$>
v .: "apiEndpoint" <*>
v .: "namespace" <*>
v .: "credentials" <*>
v .: "cluster-access" <*>
v .: "agent"

parseJSON invalid = typeMismatch "Config" invalid

data RawClusterAccess = RawServiceAccount
| RawClusterAccess {
_server :: Text
, _namespace :: Text
, _credentials :: RawCredentials
} deriving (Show, Generic)

data RawCredentials = RawCredentials {
_certificate :: Text,
_privateKey :: Text
} deriving (Show, Generic)
instance FromJSON RawClusterAccess where
parseJSON (Object v) = RawClusterAccess <$>
v .: "server" <*>
v .: "namespace" <*>
v .: "credentials"
parseJSON (String "ServiceAccount") = pure RawServiceAccount

parseJSON invalid = typeMismatch "ClusterAccess" invalid


data RawCredentials = RawToken Text
| RawClientCertificate {
_certificate :: Text,
_privateKey :: Text
} deriving (Show, Generic)

instance FromJSON RawCredentials where
parseJSON (Object v) = RawCredentials <$>
v .: "certificate" <*>
v .: "privateKey"
parseJSON (Object v) = RawToken
<$> v .: "token"
<|> RawClientCertificate
<$> v .: "certificate"
<*> v .: "private-key"

parseJSON invalid = typeMismatch "Credentials" invalid
Loading

0 comments on commit 70d435a

Please sign in to comment.