diff --git a/cmd/nmi/main.go b/cmd/nmi/main.go index 69856431..83f23eef 100644 --- a/cmd/nmi/main.go +++ b/cmd/nmi/main.go @@ -41,6 +41,7 @@ var ( findIdentityRetryIntervalInSeconds = pflag.Int("find-identity-retry-interval", defaultlistPodIDsRetryIntervalInSeconds, "Retry interval to find assigned identities in seconds") enableProfile = pflag.Bool("enableProfile", false, "Enable/Disable pprof profiling") enableScaleFeatures = pflag.Bool("enableScaleFeatures", false, "Enable/Disable features for scale clusters") + blockInstanceMetadata = pflag.Bool("block-instance-metadata", false, "Block instance metadata endpoints") ) func main() { @@ -75,7 +76,7 @@ func main() { exit := make(<-chan struct{}) client.Start(exit) *forceNamespaced = *forceNamespaced || "true" == os.Getenv("FORCENAMESPACED") - s := server.NewServer(*forceNamespaced, *micNamespace) + s := server.NewServer(*forceNamespaced, *micNamespace, *blockInstanceMetadata) s.KubeClient = client s.MetadataIP = *metadataIP s.MetadataPort = *metadataPort diff --git a/docs/readmes/README.featureflags.md b/docs/readmes/README.featureflags.md index 8f8aaa6d..95772d3e 100644 --- a/docs/readmes/README.featureflags.md +++ b/docs/readmes/README.featureflags.md @@ -20,4 +20,25 @@ node/VMSS. > Available from 1.5.3 release Aad-pod-identity has a new flag clientQps which can be used to control the total number of client operations performed per second -to the API server by MIC. \ No newline at end of file +to the API server by MIC. + +## Block Instance Metadata flag + +The Azure Metadata API includes endpoints under `/instance/metadata` which +provide information about the virtual machine. You can see examples of this +endpoint in [the Azure documentation](https://docs.microsoft.com/en-us/azure/virtual-machines/linux/instance-metadata-service#retrieving-all-metadata-for-an-instance). + +Some of the information returned by this endpoint may be considered sensitive +or secret. The response includes information on the operating system and image, +tags, resource IDs, network, and VM custom data. + +This information is legitimately useful for many use cases, but also presents a +risk. If an attacker can exploit a vulnerability that allows them to read from +this endpoint, they may be able to access sensitive information even if the +vulnerable Pod does not use Managed Identity. + +The `blockInstanceMetadata` flag for NMI will intercept any requests to this +endpoint from Pods which are not using host networking and return an HTTP 403 +Forbidden response. This flag is disabled by default to maximize compatibility. +Users are encouraged to determine if this option is relevant and beneficial for +their use cases. diff --git a/docs/readmes/README.instance-metadata.md b/docs/readmes/README.instance-metadata.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/readmes/README.md b/docs/readmes/README.md index 75dbd725..3b1df4d6 100644 --- a/docs/readmes/README.md +++ b/docs/readmes/README.md @@ -9,4 +9,4 @@ # Others -1. [Pod Security Policy (PSP)](README.pod-security-policy.md) \ No newline at end of file +1. [Pod Security Policy (PSP)](README.pod-security-policy.md) diff --git a/pkg/nmi/server/server.go b/pkg/nmi/server/server.go index 2793e46d..60caf936 100644 --- a/pkg/nmi/server/server.go +++ b/pkg/nmi/server/server.go @@ -45,6 +45,7 @@ type Server struct { IsNamespaced bool MICNamespace string Initialized bool + BlockInstanceMetadata bool ListPodIDsRetryAttemptsForCreated int ListPodIDsRetryAttemptsForAssigned int @@ -58,10 +59,11 @@ type NMIResponse struct { } // NewServer will create a new Server with default values. -func NewServer(isNamespaced bool, micNamespace string) *Server { +func NewServer(isNamespaced bool, micNamespace string, blockInstanceMetadata bool) *Server { return &Server{ - IsNamespaced: isNamespaced, - MICNamespace: micNamespace, + IsNamespaced: isNamespaced, + MICNamespace: micNamespace, + BlockInstanceMetadata: blockInstanceMetadata, } } @@ -74,6 +76,9 @@ func (s *Server) Run() error { mux.Handle("/metadata/identity/oauth2/token/", appHandler(s.msiHandler)) mux.Handle("/host/token", appHandler(s.hostHandler)) mux.Handle("/host/token/", appHandler(s.hostHandler)) + if s.BlockInstanceMetadata { + mux.Handle("/metadata/instance", http.HandlerFunc(forbiddenHandler)) + } mux.Handle("/", appHandler(s.defaultPathHandler)) log.Infof("Listening on port %s", s.NMIPort) @@ -458,6 +463,11 @@ func (s *Server) defaultPathHandler(logger *log.Entry, w http.ResponseWriter, r w.Write(body) } +// forbiddenHandler responds to any request with HTTP 403 Forbidden +func forbiddenHandler(w http.ResponseWriter, r *http.Request) { + http.Error(w, "Request blocked by AAD Pod Identity NMI", http.StatusForbidden) +} + func copyHeader(dst, src http.Header) { for k, vv := range src { for _, v := range vv {