Documentation

Service authorization

About service authorization

Aporeto enables a rich set of authorization capabilities for services both for process-to-process authorization as well as user-to-process authorization. Essential to the platform is that all security functions (authentication, authorization, and encryption) are delegated to the Aporeto enforcers and managed globally across enterprise applications. Complex functions such as certificate issuance, renewal, authentication rules, authorization rules, and encryption parameters become easy policy decisions.

Theory of operation

The following picture illustrate the delegation of security functions to enforcers and a common deployment scenario.

Common enforcer deployment

The Aporeto enforcer is transparently inserted in front of an application. Outgoing traffic to other services is captured by the enforcer. Incoming traffic towards services that the enforcer protects is again transparently sent to the enforcer.

Note that even traffic to external services that are not protected by Aporeto can be configured to be transparently sent through the enforcer in order to guarantee outgoing policy compliance. Similarly traffic coming from external users goes through the enforcer and it guarantees that all traffic is authorized. In this case, external users must present proper identity that can be cryptographically verified by the enforcers.

In general, all traffic between processing units is encrypted. In some rare occasions the enforcers will accept HTTP traffic (i.e., unencrypted) from external users, but will only allow this traffic towards public APIs.

There are some important configuration parameters in the service definition that enables significant flexibility in deployments:

  • Host: This is the DNS name where a service can be accessed. This DNS name can be a Kubernetes service, or a load balancer, or even a host FQDN.

  • IPs: Is a list of IPs where the service can be accessed. If a Host is provided above, there is no need to provide a list of IPs. The Aporeto system will automatically create certificates that match either the host or list of IPs. If you want to be able to access your services over IP addresses you must provide the list here so that proper certificates can be created.

  • ExposedPort: This is the port that a load balancer is listening for the service. In Kubernetes this would be the node port or load balancer port of the service. In ECS, this would be the port that the ALB/ELB is listening on. In Docker deployments this could be the host port where a container port is forwarded to. So, assuming that the service is exposed as http://www.example.com, your other containers that are accessing the service can simply issue an HTTP request to the same URI. All TLS functions are transparently handled.

  • Port: This is the port that the actual application is listening for the corresponding service. In container deployments this is the internal port of the container. In Linux service deployments this is simply the port of the process.

  • PublicApplicationPort: In addition to the above ports the configuration can include a port where external users can access the application. Assume for example that you have an application http://www.example.com listening on port 80. The moment you deploy the enforcer the connection is upgraded to HTTPS and you would now have to access the application as https://www.example.com:80. This is both unnatural and not user friendly. You can define a public application port as 443 and now you can access the application as https://www.example.com. The enforcer will implement all the corresponding port translations. Note that enforcers can be also configured to accept HTTP-only traffic in the PublicApplicationPort. This is needed in some cases where you need to expose public APIs without encryption, as for example in the case of load balancers doing health checks on the application.

The hosts, ports, and IPs essentially determine the network layer of the service and how the service can be accessed. There are additional options that allow you to customize the certificates and TLS options of the service. In general you can leave this to the default settings unless you want to implement special use cases.

Services and certificates for user authentication

When the Aporeto enforcer is configured in front of an application as an HTTP service it can provide user authorization capabilities. In this case it mandates the use of TLS.

Remember the following terminology:

  • Server certificate: the certificate that the server will advertise to clients. There can be multiple certificates that a server advertises depending on the type of client. For example, when process protected by Aporeto are communicating the certificate that is advertised by the server is the one created by Aporeto. When a user is accessing the application over the PublicApplicationPort the certificate that is advertised can be either the Aporeto issued certificate or a server certificate. Assume for example that you create a service as https://www.example.com and get a certificate from a public certificate authority. In this case, you would like clients to receive this certificate when they try to access your service. The public server certificates are provided through the TLSCertificate and TLSCertificateKey parameters. The TLSType parameter allows you to define the type of certificate that can be advertised:

    • Aporeto: the server will advertise the Aporeto issued certificate.
    • External: the server will advertise an external certificate that is provided in the TLSCertificate and TLSCertificateKey parameters.
    • LetsEncrypt: will guide enforcers to automatically derive public certificates from Let’s Encrypt. This will work if a process is directly exposed to the internet and there is no Load Balancer that is actually terminating the user requests. (This will be implemented in the next release).
    • None: This is indicates no TLS at all on the Public Application Port. This can be used for Load Balancer health checks. It is not recommended to allow user access over HTTP. Even in that case though, only the public APIs will be accessible. If an API requires authorization it cannot be accessible over an HTTP service.
  • Client certificate: is the certificate that a client can present to the server to authenticate itself. Client certificates can be issued by the Aporeto system or by any third party certificate authority. If the client certificate is issued by a CA other than Aporeto, then you need to configure in the system the CA certificate chain that can validate these certificates.

  • Client Root CA is the CA that issues the client certificates. If it is Aporeto it is auto-discovered. If it is an external CA, the user must provide the corresponding CA certificate chain. The server will use this chain to validate and authenticate the client certificate. The chain is provided through the MTLSCertificateAuthority parameter.

By default every service (processing unit) is provided with a server certificate issued by Aporeto. This certificate combines several attributes in the subject alternative name (SAN) such as:

  • IP addresses of the process as it can be accessed by external systems. These is essentially the list of IPs from the corresponding attribute plus the list of local IPs discovered by the enforcers.

  • DNS names as they are defined in a service definition.

  • SPIFFE URIs based on the namespace and the service.

For example, for a service with name bar configured with host name myservice.com, IP addresses 10.1.1.2 and 10.1.1.3 in the namespace /foo the issued certificate will include:

  • SAN IPs: 10.1.1.2, 10.1.1.3, 127.0.0.1, and IPs of the local host.

  • SAN DNS: myservice.com

  • SAN URI: spiffe://aporeto/foo/bar

Supported client authorization methods

When a client that is not protected by Aporeto (i.e., an external user) tries to access an Aporeto-protected service it must authenticate itself. Note, that there is no authentication needed for public APIs. A client can authenticate with the methods below that are defined as the AuthorizationMethod of the service:

  • MTLS: By presenting a certificate that is signed by a trusted CA, the server can validate the client. This is also referred as mutual TLS. Note that the MTLSCertificateAuthority must be defined if the client certificates are not issued by Aporeto.

  • JWT: The client can also present a JWT that is signed by a public key that the enforcer can trust. The JWT must be presented in the Authorization header as Authorization: Bearer $JWT. In this case, the enforcer must know the signing certificate of the JWT, since we assume that these are PKI JWTs. The JWT signing certificate can be configured using the JWTSigningCert attribute.

  • OpenID Connect (OIDC): This is also a JWT-based client authentication, but in this case the JWT is dynamically issued and validated through an identity provider. The enforcer will force clients to get a JWT from the identity provider and will verify the validity of the token by communicating directly with the identity provider.

  • IP address: There is a less secure method for identifying a client through its IP address. This mode is not recommended and it is only supported for some very specific use cases where you want to limit access to APIs based on IP addresses. In general we recommend against using IP addressers as a client identifier since they can be easily spoofed.

In all of the above methods, the attributes provided by the client are normalized to a list of claims. The claims are then matched with the scopes defined for every API call and access is only permitted when they match.

Processing unit to processing unit authorization

The first use case we discuss is processing unit to processing unit authorization. In order to achieve that we need to define a set of service objects. Let us assume that service processing units include a tag app=service and the client processing units include a tag app=client. What we want to achieve is that client processing units have access to the /internal/* API endpoint of the service. In the first example we will assume that the service is accessed over a layer 3 load balancer (i.e., it doesn’t terminate TCP sessions) as is the case with simple Kubernetes services.

  • HTTPResourceSpec is needed in all API service definitions and it defines the REST API that a service exposes. The specification details a list of URIs and associated permissions for each URI. An example specification is below. It defines three endpoints /admin/*, /public/* and /internal/* and it associates different scopes with these endpoints. Note that the /public/* endpoint is given public access and thus any client will be allowed to access this API. The specification allows wild-chars. * indicates any string expansion. ? indicates that one level of the URI can be ignored. For example an endpoint /a/?/c will match /a/b/c/ and /a/d/c but it will not match /a/b/d/c.

    name: apispec
    associatedTags:
    - spec:api=demo
    description: My Demo API Service
    endpoints:
    - URI: /admin/*
    methods:
    - GET
    public: false
    scopes:
    - scope:admin
    - URI: /internal/*
    methods:
    - GET
    public: false
    scopes:
    - data:organization=example
    - URI: /public/*
    methods:
    - GET
    public: true
    scopes: []
    
  • Service: For a simple processing unit to processing unit communication the service definition needs minimal configuration as below. You need to define the host, IPs, port, and exposedPort. In addition you need to define a selector for the exposedAPIs of the service and a selector for the processing units that are implementing the service.

    name: Example Service
    description: "Simple service definition"
    hosts:
    - myservice.example.com
    IPs: []
    exposedAPIs:
    - - spec:api=demo
    exposedPort: 8000
    port: 80
    selectors:
    - - app=service
    type: HTTP
    
  • TokenScopePolicies: In order for client processing units to be able to access the restricted APIs you must also define a token scope policy that associates scopes with specific processing units. In other words you have to define for all the client processing units what types of scopes are they allowed to access. This is the same type of operation as assigning scopes to users in an identity provider. An example token scope policy is below and it defines that all processing units in a given namespace will have access to the scope app=internal.

    name: Example Scopes
    assignedScopes:
    - app=internal
    subject:
    - - $namespace=/apomux/services
    
  • Network policy: Note that you still need to define a network policy in order to allow processing unit to processing unit communication at layer 3. In most cases this already defined by your organization. An example network policy below allows access between any two processing units in the same namespace.

    - name: fallback namespace
    action: Allow
    encryptionEnabled: false
    object:
    - - $namespace=/apomux/services
    subject:
    - - $namespace=/apomux/services
    

At this point processing unit to processing unit communication in the corresponding API should be allowed.

Processing unit to processing unit authorization with layer 7 load balancers

Let’s assume now that the communication between the processing units is over a layer 7 load balancer that actually terminates TCP connections. In this case, we need to enhance the service definition with some additional configuration parameters. Specifically, outgoing traffic from the clients must now trust the certificate authority that signed the certificate of the load balancer. In order to achieve this, we need to enhance the service definition by adding the parameter TrustedCertificateAuthorities. The service definition looks like below:

name: Example Service
description: "Simple service definition"
hosts:
- myservice.example.com
IPs: []
exposedAPIs:
- - spec:api=demo
exposedPort: 8000
port: 80
selectors:
- - app=service
type: HTTP
trustedCertificateAuthorities: |-
  -----BEGIN CERTIFICATE-----
  MIIBqTCCAVCgAwIBAgIRAOyNIvUnmNvn1edRSdl1gsUwCgYIKoZIzj0EAwIwKjEZ
  .....
  -----END CERTIFICATE-----

With this definition when clients initiate a connection to this service they will know what the certificate authority is trusted. Thus, when the load balancer presents a certificate on behalf of the service signed by this authority, the clients will trusted. Note, that the load balancer must also trust the Aporeto certificate authority since it will be communicating with TLS to the destination processing units. This should be configurable through your load balancer configuration.

User authorization

In the above example we only enabled processing unit to processing unit communication with and without a load balancer. Let us assume now that we also want to give user access to the service. Specifically we want all users to have access to the /public/* APIs, but we want only “admin” users to have access to the /admin/* API. In order to achieve that we will need an API authorization and we will define a public application port where this policy will apply. In the sections below we will illustrate the configuration of user authorization for the various options.

X.509-based authorization

Mutual TLS authorization for clients

You can enable mutual TLS authorization so that clients can identify themselves with a certificate. You can do that by setting the AuthorizationType to MTLS.

NOTE

Your client certificates must be created with Service Usage: Web Client. You cannot use server certificates for client authorization.

When a client identifies itself with a certificate, the following standard parameters of the certificate are converted to claims:

  • common name (user)
  • email
  • organization
  • organizational unit (OU)

You can write an API authorization that matches any of the above claims.

Example: email=joe@acme.com or organization=acme.

X.509 certificate-based authorization with Aporeto certificates (MTLS)

We will enhance the service definition and define that user authorization is done over TLS, and the public application port is 443. In this case user certificates are issued and signed by the Aporeto root CA. The service configuration will look like below. Note that we added authorizationType as MTLS and TLSType as Aporeto.

name: Example Service
description: Simple service definition
hosts:
- myservice.example.com
IPs: []
exposedAPIs:
- - spec:api=demo
exposedPort: 8000
port: 80
publicApplicationPort: 443
selectors:
- - app=service
type: HTTP
authorizationType: MTLS
TLSType: Aporeto
trustedCertificateAuthorities: |-
  -----BEGIN CERTIFICATE-----
  MIIBqTCCAVCgAwIBAgIRAOyNIvUnmNvn1edRSdl1gsUwCgYIKoZIzj0EAwIwKjEZ
  .....
  -----END CERTIFICATE-----

If a user access the service now at https://myservice.example.com and presents a certificate issued by Aporeto that belongs in the organization example it will be given access to the internal API. All users, even those that do not present a certificate will have access to the public APIs. Note, however that in this case your clients must trust the Aporeto certificate authority.

X.509 certificate-based authorization with Aporeto certificates and a public service

Obviously trusting the Aporeto certificates is not always desirable, especially of the organization has certificates for the service signed by a public CA. In this case, we can modify the service to advertise the external service certificates to clients and avoid any additional client configuration. In the enhanced service below we define that the certificate that the service should present to clients is the one provided in the definition. We modify the TLSType to external and provide the necessary certificate and key.

name: Example Service
description: Simple service definition
hosts:
- myservice.example.com
IPs: []
exposedAPIs:
- - spec:api=demo
exposedPort: 8000
port: 80
publicApplicationPort: 443
selectors:
- - app=service
type: HTTP
authorizationType: MTLS
TLSType: External
trustedCertificateAuthorities: |-
  -----BEGIN CERTIFICATE-----
  MIIBqTCCAVCgAwIBAgIRAOyNIvUnmNvn1edRSdl1gsUwCgYIKoZIzj0EAwIwKjEZ
  .....
  -----END CERTIFICATE-----
TLSCertificate: |-
  -----BEGIN CERTIFICATE-----
  MIIBqTCCAVCgAwIBAgIRAOyNIvUnmNvn1edRSdl1gsUwCgYIKoZIzj0EAwIwKjEZ
  MBcGA1UEChMQQWNtZSBFbnRlcnByaXNlczENMAsGA1UEAxMEcm9vdDAeFw0xODEw
  MjAwMTA4MjNaFw0yODA4MjgwMTA4MjNaMDExGTAXBgNVBAoTEEFjbWUgRW50ZXJw
  cmlzZXMxFDASBgNVBAMTC2RlbW8tc2VydmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0D
  AQcDQgAEWs2oa3+xzf9ErItw5N5j6pVztjeBdSWYA0bfsj6I9faFP97uCuldBpvr
  yud86Hze3FqFOX9w9pyZJctSSL0JFaNQME4wDgYDVR0PAQH/BAQDAgWgMB0GA1Ud
  JQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMA8GA1UdEQQI
  MAaHBKwRAAIwCgYIKoZIzj0EAwIDRwAwRAIgAmQE2bROtEQbcEO6yyBgE0b96obj
  StNdovEnWCEYDZoCIH/0BFWD7OQwHigPCwSvfNb2FPgQW7/PC5DHYgfQEtmC
  -----END CERTIFICATE-----
TLSCertificateKey: |-
  -----BEGIN EC PRIVATE KEY-----
  MHcCAQEEIBbe+8Kwfl1KmCTlUky67RAcNAYwnh7dExuc2IGmiLXooAoGCCqGSM49
  AwEHoUQDQgAEWs2oa3+xzf9ErItw5N5j6pVztjeBdSWYA0bfsj6I9faFP97uCuld
  Bpvryud86Hze3FqFOX9w9pyZJctSSL0JFQ==
  -----END EC PRIVATE KEY-----

X.509 certificate-based authorization with privately signed certificates and a public service

In several instances an enterprise would have issued client certificates based on their own private certificate authority. User authorization can work with this option as well by configuring the service to accept client certificates signed by this private certificate authority. In order to achieve that we need to enhance the service definition above and provide the certificate chain for this private CA. We do that by populating the MTLSCertificateAuthority.

name: Example Service
description: Simple service definition
hosts:
- myservice.example.com
IPs: []
exposedAPIs:
- - spec:api=demo
exposedPort: 8000
port: 80
publicApplicationPort: 443
selectors:
- - app=service
type: HTTP
authorizationType: MTLS
TLSType: External
trustedCertificateAuthorities: |-
  -----BEGIN CERTIFICATE-----
  MIIBqTCCAVCgAwIBAgIRAOyNIvUnmNvn1edRSdl1gsUwCgYIKoZIzj0EAwIwKjEZ
  .....
  -----END CERTIFICATE-----
TLSCertificate: |-
  -----BEGIN CERTIFICATE-----
  MIIBqTCCAVCgAwIBAgIRAOyNIvUnmNvn1edRSdl1gsUwCgYIKoZIzj0EAwIwKjEZ
  ...
  -----END CERTIFICATE-----
TLSCertificateKey: |-
  -----BEGIN EC PRIVATE KEY-----
  MHcCAQEEIBbe+8Kwfl1KmCTlUky67RAcNAYwnh7dExuc2IGmiLXooAoGCCqGSM49
  ...
  -----END EC PRIVATE KEY-----
MTLSCertificateAuthority: |-
    -----BEGIN CERTIFICATE-----
  MIIBqTCCAVCgAwIBAgIRAOyNIvUnmNvn1edRSdl1gsUwCgYIKoZIzj0EAwIwKjEZ
  .....
  -----END CERTIFICATE-----

OIDC-based user authorization

The Aporeto system provides a seamless integration with OpenID Connect providers for user-to-service authorization. The enforcer will completely offload all the OIDC negotiation protocol and authorization decisions from your application logic. Your application can simply implement an unprotected API exposed without TLS and the enforcer takes care of everything else. Refer to OIDC for users of Aporeto-protected applications for more details.

JWT/PKI-based user authorization

The Aporeto enforcer also supports a JWT-based method for user authorization. In this case the assumption is that the JWTs have been signed by the private key of the issuer and the public key is well known. For example the Aporeto tokens are such JWTs and they are signed by Aporeto. In order to enable such authorization you can use a service definition as below:

name: Example Service
description: Simple service definition
hosts:
- myservice.example.com
IPs: []
exposedAPIs:
- - spec:api=demo
exposedPort: 8000
port: 80
publicApplicationPort: 443
selectors:
- - app=service
type: HTTP
authorizationType: JWT
TLSType: External
JWTSigningCertificate: |-
  -----BEGIN CERTIFICATE-----
  MIIBqTCCAVCgAwIBAgIRAOyNIvUnmNvn1edRSdl1gsUwCgYIKoZIzj0EAwIwKjEZ
  .....
  -----END CERTIFICATE-----

Header mapping for user authorization

In some cases applications might need to see the user attributes of the authorized user for additional validations and checks. The platform provides the ability to map any claims presented as an identity by the user to a corresponding HTTP header that is passed to the downstream application. In order to do that you can use the claimsToHTTPHeaderMappings attribute of the service and define a list of mappings.

In the following example the email claim of an incoming token will provided with an X-User HTTP header to the application:

name: Example Service
description: "Simple service definition"
hosts:
- myservice.example.com
IPs: []
exposedAPIs:
- - spec:api=demo
exposedPort: 8000
port: 80
publicApplicationPort: 443
selectors:
- - app=service
type: HTTP
authorizationType: JWT
TLSType: External
claimsToHTTPHeaderMappings:
- claimName: email
  targetHTTPHeader: X-User
JWTSigningCertificate: |-
  -----BEGIN CERTIFICATE-----
  MIIBqTCCAVCgAwIBAgIRAOyNIvUnmNvn1edRSdl1gsUwCgYIKoZIzj0EAwIwKjEZ
  .....
  -----END CERTIFICATE-----