-
Notifications
You must be signed in to change notification settings - Fork 59
/
Config.hs
164 lines (150 loc) · 6.55 KB
/
Config.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
{-# LANGUAGE OverloadedStrings #-}
module Kubernetes.Client.Config
( KubeConfigSource(..)
, addCACertData
, addCACertFile
, applyAuthSettings
, clientHooksL
, defaultTLSClientParams
, disableServerCertValidation
, disableServerNameValidation
, disableValidateAuthMethods
, loadPEMCerts
, mkInClusterClientConfig
, mkKubeClientConfig
, newManager
, onCertificateRequestL
, onServerCertificateL
, parsePEMCerts
, serviceAccountDir
, setCAStore
, setClientCert
, setMasterURI
, setTokenAuth
, tlsValidation
)
where
import qualified Kubernetes.OpenAPI.Core as K
import Control.Applicative ((<|>))
import Control.Exception.Safe (MonadThrow, throwM)
import Control.Monad.IO.Class (MonadIO, liftIO)
import qualified Data.ByteString as B
import qualified Data.ByteString.Base64 as B64
import qualified Data.ByteString.Lazy as LazyB
import Data.Either.Combinators
import Data.Function ((&))
import Data.Maybe
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.Text.IO as T
import Data.Yaml
import Kubernetes.Client.Auth.ClientCert
import Kubernetes.Client.Auth.GCP
import Kubernetes.Client.Auth.OIDC
import Kubernetes.Client.Auth.Token
import Kubernetes.Client.Internal.TLSUtils
import Kubernetes.Client.KubeConfig
import Network.Connection (TLSSettings (..))
import qualified Network.HTTP.Client as NH
import Network.HTTP.Client.TLS (mkManagerSettings)
import qualified Network.TLS as TLS
import System.Environment (getEnv)
import System.FilePath
data KubeConfigSource = KubeConfigFile FilePath
| KubeConfigCluster
{-|
Creates 'NH.Manager' and 'K.KubernetesClientConfig' for a given
'KubeConfigSource'. It is recommended that multiple 'kubeClient' invocations
across an application share an 'OIDCCache', this makes sure updation of OAuth
token is synchronized across all the different clients being used.
-}
mkKubeClientConfig
:: OIDCCache
-> KubeConfigSource
-> IO (NH.Manager, K.KubernetesClientConfig)
mkKubeClientConfig oidcCache (KubeConfigFile f) = do
kubeConfig <- decodeFileThrow f
masterURI <- server <$> getCluster kubeConfig
& either (const $ pure "localhost:8080") return
tlsParams <- configureTLSParams kubeConfig (takeDirectory f)
clientConfig <- K.newConfig & fmap (setMasterURI masterURI)
(tlsParamsWithAuth, clientConfigWithAuth) <-
case getAuthInfo kubeConfig of
Left _ -> return (tlsParams,clientConfig)
Right (_, auth) -> applyAuthSettings oidcCache auth (tlsParams, clientConfig)
mgr <- newManager tlsParamsWithAuth
return (mgr, clientConfigWithAuth)
mkKubeClientConfig _ (KubeConfigCluster) = mkInClusterClientConfig
-- |Creates 'NH.Manager' and 'K.KubernetesClientConfig' assuming it is being executed in a pod
mkInClusterClientConfig :: (MonadIO m, MonadThrow m) => m (NH.Manager, K.KubernetesClientConfig)
mkInClusterClientConfig = do
caStore <- loadPEMCerts $ serviceAccountDir ++ "/ca.crt"
defTlsParams <- liftIO defaultTLSClientParams
mgr <- liftIO . newManager . setCAStore caStore $ disableServerNameValidation defTlsParams
tok <- liftIO . T.readFile $ serviceAccountDir ++ "/token"
host <- liftIO $ getEnv "KUBERNETES_SERVICE_HOST"
port <- liftIO $ getEnv "KUBERNETES_SERVICE_PORT"
cfg <- setTokenAuth tok . setMasterURI (T.pack $ "https://" ++ host ++ ":" ++ port) <$> liftIO K.newConfig
return (mgr, cfg)
-- |Sets the master URI in the 'K.KubernetesClientConfig'.
setMasterURI
:: T.Text -- ^ Master URI
-> K.KubernetesClientConfig
-> K.KubernetesClientConfig
setMasterURI masterURI kcfg =
kcfg { K.configHost = (LazyB.fromStrict . T.encodeUtf8) masterURI }
-- |Creates a 'NH.Manager' that can handle TLS.
newManager :: TLS.ClientParams -> IO NH.Manager
newManager cp = NH.newManager (mkManagerSettings (TLSSettings cp) Nothing)
serviceAccountDir :: FilePath
serviceAccountDir = "/var/run/secrets/kubernetes.io/serviceaccount"
configureTLSParams :: Config -> FilePath -> IO TLS.ClientParams
configureTLSParams cfg dir = do
defaultTLS <- defaultTLSClientParams
withCACertData <- addCACertData cfg defaultTLS
withCACertFile <- addCACertFile cfg dir withCACertData
return $ tlsValidation cfg withCACertFile
tlsValidation :: Config -> TLS.ClientParams -> TLS.ClientParams
tlsValidation cfg tlsParams =
case getCluster cfg of
Left _ -> tlsParams
Right c ->
case insecureSkipTLSVerify c of
Just True -> disableServerCertValidation tlsParams
_ -> tlsParams
addCACertData :: (MonadThrow m) => Config -> TLS.ClientParams -> m TLS.ClientParams
addCACertData cfg tlsParams =
let eitherCertText = getCluster cfg
& (>>= (maybeToRight "cert data not provided" . certificateAuthorityData))
in case eitherCertText of
Left _ -> pure tlsParams
Right certBase64 -> do
certText <- B64.decode (T.encodeUtf8 certBase64)
& either (throwM . Base64ParsingFailed) pure
updateClientParams tlsParams certText
& either throwM return
addCACertFile :: Config -> FilePath -> TLS.ClientParams -> IO TLS.ClientParams
addCACertFile cfg dir tlsParams = do
let eitherCertFile = getCluster cfg
>>= maybeToRight "cert file not provided" . certificateAuthority
& fmap T.unpack
& fmap (dir </>)
case eitherCertFile of
Left _ -> return tlsParams
Right certFile -> do
certText <- B.readFile certFile
return
$ updateClientParams tlsParams certText
& (fromRight tlsParams)
applyAuthSettings
:: OIDCCache
-> AuthInfo
-> (TLS.ClientParams, K.KubernetesClientConfig)
-> IO (TLS.ClientParams, K.KubernetesClientConfig)
applyAuthSettings oidcCache auth input = fromMaybe (pure input)
$ clientCertFileAuth auth input
<|> clientCertDataAuth auth input
<|> tokenAuth auth input
<|> tokenFileAuth auth input
<|> gcpAuth auth input
<|> cachedOIDCAuth oidcCache auth input