# Support Ssl Client Certificate in Build/Release Job (TFS On-Prem Only)

## Goals

  - Support agent configure and connect to TFS use ssl client certificate
  - Support get source in Build job and download artifact in Release job works with ssl client certificate
  - Provide documentation and scripts to help customer prepare all pre-requrements before configuration
  - Expose ssl client certificate information in vsts-task-lib for task author

## Pre-requirements

  - CA certificate(s) in `.pem` format (This should contains the public key and signature of the CA certificate, you need put the root ca certificate and all your intermediate ca certificates into one `.pem` file)  
  - Client certificate in `.pem` format (This should contains the public key and signature of the Client certificate)  
  - Client certificate private key in `.pem` format (This should contains only the private key of the Client certificate)  
  - Client certificate archive package in `.pfx` format (This should contains the signature, public key and private key of the Client certificate)  
  - Use `SAME` password to protect Client certificate private key and Client certificate archive package, since they both have client certificate's private key  
  
The Build/Release agent is just xplat tool runner, base on what user defined in their Build/Release definition, invoke different tools to finish user's job. So the client certificate support is not only for the agent infrastructure but most important for all different tools and technologies user might use during a Build/Release job.
```
   Ex:
      Clone Git repository from TFS use Git
      Sync TFVC repository from TFS use Tf.exe on Windows and Tf on Linux/OSX
      Write customer Build/Release task that make REST call to TFS use VSTS-Task-Lib (PowerShell or Node.js)
      Consume Nuget/NPM packages from TFS package management use Nuget.exe and Npm
      [Future] Publish and consume artifacts from TFS artifact service use Drop.exe (artifact) and PDBSTR.exe (symbol)
```


You can use `OpenSSL` to get all pre-required certificates format ready easily as long as you have all pieces of information.

### Windows

Windows has a pretty good built-in certificate manger, the `Windows Certificate Manager`, it will make most Windows based application deal with certificate problem easily. However, most Linux background application (Git) and technologies (Node.js) won't check the `Windows Certificate Manager`, they just expect all certificates are just a file on disk.  

Use the following step to setup pre-reqs on Windows, assume you already installed your corporation's `CA root cert` into local machine's `Trusted CA Store`, and you have your client cert `clientcert.pfx` file on disk and you know the `password` for it.  
  - Export CA cert from `Trusted Root CA Store`, use `Base64 Encoding X.509 (.CER)` format, name the export cert to something like `ca.pem`.  
  - Export any intermediate CA cert from `Intermediate CA Store`, use `Base64 Encoding X.509 (.CER)` format, name the export cert to something like `ca_inter_1/2/3.pem`. Concatenate all intermediate ca certs into `ca.pem`, your `ca.pem` might looks like following:  
```
-----BEGIN CERTIFICATE----- 
(Your Root CA certificate: ca.pem) 
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE----- 
(Your Intermediate CA certificate: ca_inter_1.pem) 
-----END CERTIFICATE-----
...
-----BEGIN CERTIFICATE----- 
(Your Intermediate CA certificate: ca_inter_n.pem) 
-----END CERTIFICATE-----
```  
  - Extract Client cert and Client cert private key from `.pfx` file. You need `OpenSSL` to do this, you either install `OpenSSL for Windows` or just use `Git Bash`, since `Git Bash` has `OpenSSL` baked in.
```
Inside Git Bash:    
  Extract client-cert.pem
  openssl pkcs12 -in clientcert.pfx -passin pass:<YOURCERTPASSWORD> -nokeys -clcerts -out client-cert.pem
      
  Extract client-cert-key.pem, this will get password protected
  openssl pkcs12 -in clientcert.pfx -passin pass:<YOURCERTPASSWORD> -nocerts -out client-cert-key.pem -passout pass:<YOURCERTPASSWORD> 
```
    
At this point, you should have all required pieces `ca.pem`, `client-cert.pem`, `client-cert-key.pem` and `clientcert.pfx`.

### No-Windows

As I mentioned before, most Linux backgroud application just expect all certificate related files are on disk, and use `OpenSSL` to deal with cert is quiet common on Linux, so I assume for customer who wants to setup Build/Release agent on Linux already has `ca.pem`, `client-cert.pem` and `client-cert-key.pem` in place. So the only missing piece should be the client cert archive `.pfx` file.  
```
From Terminal:
openssl pkcs12 -export -out client-cert-archive.pfx -passout pass:<YOURCERTPASSWORD> -inkey client-cert-key.pem -in client-cert.pem -passin pass:<YOURCERTPASSWORD> -certfile CA.pem
```

## Configuration  

**In order to get directory/file ACL setup correctly, make sure you put all certificates files under agent root directory**

Pass `--sslcacert`, `--sslclientcert`, `--sslclientcertkey`. `--sslclientcertarchive` and `--sslclientcertpassword` during agent configuration.   
Ex:
```batch
.\config.cmd --sslcacert enterprise.pem --sslclientcert client.pem --sslclientcertkey clientcert-key-pass.pem --sslclientcertarchive clientcert-2.pfx --sslclientcertpassword "test123"
```  

We store your client cert private key password securely on each platform.  
Ex:
```
Windows: Windows Credential Store
OSX: OSX Keychain
Linux: Encrypted with symmetric key based on machine id
```

## How agent handle client cert within a Build/Release job

After configuring client cert for agent, agent infrastructure will start talk to VSTS/TFS service using the client cert configured.  

Since the code for `Get Source` step in build job and `Download Artifact` step in release job are also bake into agent, those steps will also follow the agent client cert configuration.  

Agent will expose client cert configuration via environment variables for every task execution, task author need to use `vsts-task-lib` methods to retrieve back client cert configuration and handle client cert with their task.

## Get client cert configuration by using [VSTS-Task-Lib](https://github.com/Microsoft/vsts-task-lib) method

Please reference [VSTS-Task-Lib doc](https://github.com/Microsoft/vsts-task-lib/blob/master/node/docs/cert.md) for detail

## Progress
 - Agent infrastructure (you can configure and queue a build/release) [DONE]
 - Fetch git repository [DONE]
 - Fetch tfvc repository [Only supported in Windows agent]
 - Expose client cert info to task sdk [DONE]

## Self-Signed CA Certificates

I would assume there are some self-signed CA certificates along with the client certificate, however the current agent doesn't has a good way to handle self-signed CA certificates.  

The work of the client certificate support do add a `--sslcacert` option to agent configuration, but it currentlly just for some of the downstream tools your Build/Release job and not for the agent infrastructure. In order to use self-signed CA certificates with the agent, you need to maunally install all self-signed CA certificates into your OS's certificate store, like: `Windows certificate manager` on `Windows`, `OpenSSL CA store` on `Linux`. Just like you have to manually configure your browser to take those certificates. We might be able to improve this when we consume netcore 2.0 in the agent.  

The next problem is about all different downstream tools you used in your Build/Release job, the way they find CA certificates might all different.  
Ex:
 - Git (version < 2.14.x) expect a `--cainfo` option and point to the CA file.  
 - Git (version >= 2.14.x) has a config option to let Git to read CA from `Windows Certificate Manager` on `Windows`.  
 - Tf.exe expect read CA from `Windows Certificate Manager`.  
 - TEE (tf on linux) expect read CA from `Java Certificate Store`.  
 - PowerShell expect read CA from `Windows Certificate Manager`.  
 - Node.js expect a `ca` parameter on `tls.options`.  
 - Node.js (version >= 7.3) also expect an environment vairble to point to the CA file `NODE_EXTRA_CA_CERTS`, however the agent current use Node.js version 6.x which mean we can't use that envirinment variable.  

At this point, I would sugguest when you have a self-signed CA cert, please make sure the tools or technologies you used within your Build/Release works with your self-signed CA cert first, then try to configure the agent.  
In this way, even you get an error within your build/release job, you might have better idea of where is the error coming from.  

## Dev notes

I use following commands to generate certificates for testing(all commands needs to be run on same machine in admin command prompt)  

Root CA cert:  
`MakeCert -n "CN=Enterprise_issuer_2" -pe -ss Root -sr LocalMachine -sky exchange -m 6 -a sha1 -len 2048 -r -eku 1.3.6.1.5.5.7.3.2,1.3.6.1.5.5.7.3.1`  

Server cert:  
`MakeCert -n "CN=TFSAT.mycompany.com" -pe -ss My -sr LocalMachine -sky exchange -m 6 -in Enterprise_issuer_2 -is Root -ir LocalMachine -a sha1 -eku 1.3.6.1.5.5.7.3.1 -len 2048`  

Client cert:  
`MakeCert -n "CN=mycompany\ting" -pe -ss My -sr CurrentUser -sky exchange -m 6 -in Enterprise_issuer_2 -is Root -ir LocalMachine -a sha1 -eku 1.3.6.1.5.5.7.3.2 -len 2048`
