This is an automated email from the ASF dual-hosted git repository.

zrhoffman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new 3e388a3b97 Adding a Traffic Ops client method to login with a 
certificate (#7645)
3e388a3b97 is described below

commit 3e388a3b97456c79235e4a6d487afced8113e663
Author: Srijeet Chatterjee <[email protected]>
AuthorDate: Mon Jul 17 13:29:51 2023 -0600

    Adding a Traffic Ops client method to login with a certificate (#7645)
    
    * Adding a Traffic Ops client method to login with a certificate
    
    * adding changelog, godocs
    
    * adding . in changelog
    
    * try to fix gha
    
    * add sudo
    
    * wip
    
    * adding debug
    
    * adding insecure skip verify option
    
    * remove debug
---
 .github/actions/to-integration-tests/cdn.json      |  7 +++
 .github/actions/to-integration-tests/entrypoint.sh | 11 ++++
 .../to-integration-tests/intermediate.crt.pem      | 34 +++++++++++
 .../to-integration-tests/intermediate.key.pem      | 52 +++++++++++++++++
 .../actions/to-integration-tests/rootca.crt.pem    | 32 +++++++++++
 .../actions/to-integration-tests/rootca.key.pem    | 52 +++++++++++++++++
 .../actions/to-integration-tests/server.crt.pem    | 34 +++++++++++
 .../actions/to-integration-tests/server.key.pem    | 52 +++++++++++++++++
 CHANGELOG.md                                       |  1 +
 .../api/v5/client-intermediate-chain.crt.pem       | 67 ++++++++++++++++++++++
 traffic_ops/testing/api/v5/client.key.pem          | 52 +++++++++++++++++
 traffic_ops/testing/api/v5/session_test.go         | 16 +++++-
 traffic_ops/toclientlib/toclientlib.go             | 47 +++++++++++++++
 traffic_ops/traffic_ops_golang/auth/certificate.go | 11 ++--
 .../traffic_ops_golang/auth/certificate_test.go    | 12 ++--
 traffic_ops/traffic_ops_golang/login/login.go      |  2 +-
 traffic_ops/v5-client/session.go                   | 15 +++++
 17 files changed, 485 insertions(+), 12 deletions(-)

diff --git a/.github/actions/to-integration-tests/cdn.json 
b/.github/actions/to-integration-tests/cdn.json
index 0da5adbe8e..734dbb9d37 100644
--- a/.github/actions/to-integration-tests/cdn.json
+++ b/.github/actions/to-integration-tests/cdn.json
@@ -1,8 +1,15 @@
 {
+       "client_certificate_authentication" : {
+               "root_certificates_directory" : "/etc/pki/tls/traffic_ops/"
+       },
        "disable_auto_cert_deletion": false,
        "use_ims": true,
        "role_based_permissions": true,
        "traffic_ops_golang": {
+               "tls_config": {
+                       "MinVersion": 769,
+                       "ClientAuth" : 1
+               },
                "cert": "$PWD/localhost.crt",
                "key": "$PWD/localhost.key",
                "insecure": true,
diff --git a/.github/actions/to-integration-tests/entrypoint.sh 
b/.github/actions/to-integration-tests/entrypoint.sh
index 5728da8358..4150ccff9f 100755
--- a/.github/actions/to-integration-tests/entrypoint.sh
+++ b/.github/actions/to-integration-tests/entrypoint.sh
@@ -107,9 +107,20 @@ A22D22wvfs7CE3cUz/8UnvLM3kbTTu1WbbBbrHjAV47sAHjW/ckTqeo=
 -----END RSA PRIVATE KEY-----
 " > localhost.key
 
+if [[ ! -e "/etc/pki/tls/traffic_ops/" ]]; then
+       sudo mkdir -p "/etc/pki/tls/traffic_ops/"
+fi
+
 resources="$(dirname "$0")"
 envsubst <"${resources}/cdn.json" >cdn.conf
 cp "${resources}/database.json" database.conf
+sudo cp "${resources}/intermediate.crt.pem" /etc/pki/tls/traffic_ops/
+sudo cp "${resources}/intermediate.key.pem" /etc/pki/tls/traffic_ops/
+sudo cp "${resources}/server.crt.pem" /etc/pki/tls/traffic_ops/
+sudo cp "${resources}/server.key.pem" /etc/pki/tls/traffic_ops/
+sudo cp "${resources}/rootca.crt.pem" /etc/pki/tls/traffic_ops/
+sudo cp "${resources}/rootca.key.pem" /etc/pki/tls/traffic_ops/
+
 
 truncate --size=0 traffic.ops.log # Removes output from previous API versions 
and makes sure files exist
 ./traffic_ops_golang --cfg ./cdn.conf --dbcfg ./database.conf &
diff --git a/.github/actions/to-integration-tests/intermediate.crt.pem 
b/.github/actions/to-integration-tests/intermediate.crt.pem
new file mode 100644
index 0000000000..628dc1e412
--- /dev/null
+++ b/.github/actions/to-integration-tests/intermediate.crt.pem
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+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
+k98B/1M8VEu7lPOn0xN2
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/.github/actions/to-integration-tests/intermediate.key.pem 
b/.github/actions/to-integration-tests/intermediate.key.pem
new file mode 100644
index 0000000000..0ea8cdde9e
--- /dev/null
+++ b/.github/actions/to-integration-tests/intermediate.key.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+716ZP/Gc0N8+7NlzDJ3dia2luEq4yA==
+-----END PRIVATE KEY-----
\ No newline at end of file
diff --git a/.github/actions/to-integration-tests/rootca.crt.pem 
b/.github/actions/to-integration-tests/rootca.crt.pem
new file mode 100644
index 0000000000..ed11adb038
--- /dev/null
+++ b/.github/actions/to-integration-tests/rootca.crt.pem
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFjjCCA3agAwIBAgIIe/j/H9wrFrAwDQYJKoZIhvcNAQELBQAwZTELMAkGA1UE
+BhMCVVMxETAPBgNVBAgTCENvbG9yYWRvMQ8wDQYDVQQHEwZEZW52ZXIxDzANBgNV
+BAoTBkFwYWNoZTEMMAoGA1UECxMDQVRDMRMwEQYDVQQDEwpyb290LmxvY2FsMB4X
+DTIzMDcxMzAwMTUwMloXDTI0MDcxMzAwMTUwMlowZTELMAkGA1UEBhMCVVMxETAP
+BgNVBAgTCENvbG9yYWRvMQ8wDQYDVQQHEwZEZW52ZXIxDzANBgNVBAoTBkFwYWNo
+ZTEMMAoGA1UECxMDQVRDMRMwEQYDVQQDEwpyb290LmxvY2FsMIICIjANBgkqhkiG
+9w0BAQEFAAOCAg8AMIICCgKCAgEAt8hYrmpHyB3a2jf1IyDQDfEyuST4wWJgWDJF
+wRLhOyK6qM1g3/3Al8m31DxrryYoDv5NyrD6VV2Asmsf5F9kXEcxoHz5m4DgdQHj
+gthot2qR7Rlc7tOcvw3bQtGyV/p9IBZ1bimpML+43e7d45EWJGstd4fZAakzIRRl
+AieW3H2QmJGGy/LBWtnrzppN+56nxgZBUSErPdk49i/jozqksQcGT0E2oUx9J9xA
+QucJM/BG5f6fYCoQ29P9gU/uBZiGVzOePhjnmdG1mGQMiHQU6ZKUbqhG1cVo7z1t
+qqWoCyv8Fa1JAY0lchQNMISkghubw5Pr9scWEn6OeE/Z/E6mSryDXkrM4uQqE6R6
+cCmST3rchdt4qWs0zJ7rmGU1kDL0xIq6e/BeL8PBy+LSTfR3UEFv1WH+88IVvc2E
+AyfRMhYkV6sWw//S9npZmiIQDu0pvvoQZnsEXlDKopHVIQV785rce/NKqPxls0+g
+48Ph0u9ubZP2R6Ve/GNb/47k3+uMdA37gCeNW8Qg7PsGoExeRa8IrmPlBBsG9EsQ
+HzVeOIyl4X78MQYpvzxGRPmJHaRjXcC36jHY3TqjNhUUGWl1KzZXezBkdPYGKhXi
+4MG2IwdPhGMZvn4B5YP0I5SNymcdjf2n7bdK8GjIgPzElqceXvSCa1a8bnuruOvN
+xZ/v9k8CAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8w
+HQYDVR0OBBYEFHYwHShkpRb/aRYc+l9XkP7rGYRXMA0GCSqGSIb3DQEBCwUAA4IC
+AQCALWiIDM/2jPrqqgWFPoE81zohYNJ5KVYFD9jxuA6C39sj4YiM1KJcoPrbEErV
+bLbVYwXdVIJHeBkqqxDdGIB3LGsFJP6dEnDAmk3a7B6/cgljWX35UsRC++o7dZ/L
+qQUSNNVz2mUIb/m3hjwfpgDxI8Dxm7OKLqdkdHD6zBdXhtzQy5mVc1b8kU7bSIIm
+aBOdlp4qWNNtWt7zU6UjhbC1l+w0deU1jkUzmY3Jv/HX+3ouR3XxwRo2tZdCyp/8
+/90bWqyrstlq8QyMoyHewJsLksOUKyqwy4pRSSXvJL8K2pJCTtxFHPrDdVb1GHfw
+1NhSeUHstQUz2h2Y09zKKzyHsaWtrJfIruzilKBJFiVbi441qK49kMvsEiNxhqqz
+A5Ua+aeALVygWhqqWNNiEkmtwrkKgtthWCqVVkMo0XUCI/t9F3f7V0EUNEr/IuPB
+66nrfRrwuZWjlbFZS5Vd0mVP3WaIOo1/yB4yxrLzyVZIilXOcIJvMaKSs8UFF0Hc
+XBel9VoFGLqu7Wn3v7/lKvJ+z6wdJm/vd+1nZBeKMkHTzBNmrx51DZB3bq2P/VpB
+B8StvCnmOQHT16UZ+fJXPrIQCPXkqf60x1RpU8y29HcI0U6k5u27cUjmbT8uTKEE
+LyUSq66lNmgMghRJuHAgFgrI82oXaU6fgMHp6rzAeK/kgA==
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/.github/actions/to-integration-tests/rootca.key.pem 
b/.github/actions/to-integration-tests/rootca.key.pem
new file mode 100644
index 0000000000..2bbb8e0b25
--- /dev/null
+++ b/.github/actions/to-integration-tests/rootca.key.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQC3yFiuakfIHdra
+N/UjINAN8TK5JPjBYmBYMkXBEuE7IrqozWDf/cCXybfUPGuvJigO/k3KsPpVXYCy
+ax/kX2RcRzGgfPmbgOB1AeOC2Gi3apHtGVzu05y/DdtC0bJX+n0gFnVuKakwv7jd
+7t3jkRYkay13h9kBqTMhFGUCJ5bcfZCYkYbL8sFa2evOmk37nqfGBkFRISs92Tj2
+L+OjOqSxBwZPQTahTH0n3EBC5wkz8Ebl/p9gKhDb0/2BT+4FmIZXM54+GOeZ0bWY
+ZAyIdBTpkpRuqEbVxWjvPW2qpagLK/wVrUkBjSVyFA0whKSCG5vDk+v2xxYSfo54
+T9n8TqZKvINeSszi5CoTpHpwKZJPetyF23ipazTMnuuYZTWQMvTEirp78F4vw8HL
+4tJN9HdQQW/VYf7zwhW9zYQDJ9EyFiRXqxbD/9L2elmaIhAO7Sm++hBmewReUMqi
+kdUhBXvzmtx780qo/GWzT6Djw+HS725tk/ZHpV78Y1v/juTf64x0DfuAJ41bxCDs
++wagTF5FrwiuY+UEGwb0SxAfNV44jKXhfvwxBim/PEZE+YkdpGNdwLfqMdjdOqM2
+FRQZaXUrNld7MGR09gYqFeLgwbYjB0+EYxm+fgHlg/QjlI3KZx2N/aftt0rwaMiA
+/MSWpx5e9IJrVrxue6u4683Fn+/2TwIDAQABAoICAHeu0gmrjtmEj7xiipJ40OTz
+eVvW9uRBI4rsGj71A5ZWNIavUPIttxliNQPS4TGrwJSbCaABBtVG0gPe/WLkuF/b
+wTbJjGe3UvT/6OCR/AfOL//e74Vca4yEApEOLH1c09Vsqsa1/MeRY1usLfX/yxKm
+dXKNFkYdoP1e4bS4gWdPojWpN8ZjbYWzuvvNwxEkrg+ojSq/VgCuEIlKgI0RKKVc
+dMByMJh0TmEB7xtih7y6MgGfgrzGSDpQYJuwqMoeHQZ2yAuafmDOj9sQ9JR8PA4r
+9dvmXFLkf0SllWB3ButXlo+rTIrlF0imRDyfdN6CCjsgfp8z4Wzj2/X/0m6b7XBM
+LVcUXcLCYJZWMlitUQY9ke8Z4RTlEEpLQ6QoKLw9whGUYISdXR6G7GYcvbtzUmi5
+cONU186/eqYRUt7JN4UZ+DhS0s/F3rkGrOrLROYT+fFTPceXu5n14p8wu31D3QM+
+9h1L7Nz/nhe7tt1N4Lp1chzCHz9oSDZBTGizpYAgRybshhMTW+QCQLvV6kr4oUo4
+GJUWoFNI6oyuTYbW8j5gG8tyQemckQH4qAV731QUMnE5iwTukbfKK+s1ft6RP1n7
+/KoJeFXQ0hbQqRi5mT6k55AlASAZrKnI0SR6WPUyEw8FU7Vteu15KNynF0+m9rxR
+kyeoXQMHmbcI0dnoJmAZAoIBAQDwvNNV0jV4pgC0z82Gx3HM5dpfvK0jO9qR45lj
+MKOzfd0lz0SYLvtRGbJbiF/HqMPPlEW5yR2dFAABUwumBhlftz77bXz0mofkX4V2
+46lmD20QIhxXtJzeZ3sLLyEheR0tldktGmXvVd1jGJURE0iq3raFpLkMWV/cGcOR
+l0hbkKU7lASIoy7XC2a1SoxaumJcMubJu54CLsZyVYrttNGV/ub15ebHcO+kYVj8
+irDB+GrGerZGmE7RuzHlK6R7olVrcWWu+XV/pHGY88kqx5OI4tEvMFxus1XMZDur
+riYbHtpsxZ+hMTVQECwwQEZmOCcgefZKvkqGGIb+qZ8P7jl1AoIBAQDDbyP5d4TB
+xNbGXtFmgXMnHAduj5+nsRDuwiTPZof87afQ4AFnHBO6IeZ59KC8sa9HD/af+tcK
+5mRbjmOgDwsA7b7YCOX2u78j4lrSwHe7tR2MPkcIpGACEPjBX5YATBgBjQeqLoFA
+CmMZrYVCnvvcp+9ReaJe797ZHnwTGX4SNdcEeMW1cD2TkUe7B65Ei/GdeQJobwDT
+re+LIk5u6306GIKwmHJ6fzZbjsYdp9EQeCYWvEFW7FOUV20FuMUpPi2/xirGSzel
+slVi3CpRZ/nZJCH4J8IlsBWelG1EI/6pGTaT6KQCpumNd+GJV60J/q1kdNm5ga4a
+BylkwmLz+/QzAoIBAEDVec0ZKdFuU65ecKmluR5HoQI7nB1rFzhHJZ4zEUNb2F1A
+bubVcFWtJ/3CwpjQ+LtE4WphoZoWMtIofXCfH+JJRasNF6QAoix9FUKpmYulB+ha
+Z2uvdrfnAppGfRO4vIT0YGLnQyCr7U2s5yiMGvv5swpXDIco4soyHhtttM0ELZvY
+B0iNOiYsKeMVSqt8mtTd2I3n+2v3EPbl1t6h3fgZiDYdToPUBJYuqXqKF4SF1KvT
+jJeCmWeelJd1o3ywgXTIO2xePqi7ruRUc48T5MXu4AtnaN1favH65J8d7aFMM+Mr
+bgxJZmRDlsL/7HXHFbKHZi9HBGcM3/5hzDbMH6kCggEAPRTIdpjPSkx+IAPUiKge
+2HNfM0P6T0v9EdJl9NiD2h5hG9xXM+XHmJ4tzaISdsdGE/y+RSP9/+8gIKlxI75v
+3vtvcU/ep+3oAIwnfMUg5u7lbEauptnRE/Q/+xgn5rHhsuJkKeFdDinw7T6ZbsgH
+yeBG4P0WLXvk95EakFGgYIqpSEGC8YFCDZQTijYNVmkyu9ftU7yV0pMDUkYHo41A
+V17/wbfz16tYzubhrrUbl30l0mdGOKemO6/TaD1micxgOhbymYU6wDPrkflj1Iud
+wCsV3tx2RpcxOp4DRI/HcQxrTXEhiZ6nUkrMl8NqVrxdbw9CWUNZqdLhMhe7G6x8
+lwKCAQEAtgm0GnBRSHjuLs0mlvkfckrAk7/3gJAGPrMnBwbHN0o5rxMQgUd+UpGf
+SHyJVmyPLOwSI5ejObyT9RGXYNu4YttxN8wkEysAvHwiQcxAcBCy84iYJei0fCLH
+nq2WAdMtnqOBLc1+IdinTi3mlyYrzb5TBK6aputrq0zewOxDnLx/4CUvE4fxMnF3
+sWevHW5wyrMEthgOMS77qsjt4CzFqz/ZN1ZOF0nHpYtB8rsZzc2FyDjTaMvQAJ3r
+wNCB6MPzdZtc6FqislKE7pjs2z5lymhpG/FznRev38cV/TuV5Mdm+kG2Fv+U7J2L
+6W3BReGvefQrP9QpYxXGiHvE9WMoIg==
+-----END PRIVATE KEY-----
\ No newline at end of file
diff --git a/.github/actions/to-integration-tests/server.crt.pem 
b/.github/actions/to-integration-tests/server.crt.pem
new file mode 100644
index 0000000000..a2cf925348
--- /dev/null
+++ b/.github/actions/to-integration-tests/server.crt.pem
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+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
+q74P
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/.github/actions/to-integration-tests/server.key.pem 
b/.github/actions/to-integration-tests/server.key.pem
new file mode 100644
index 0000000000..3275d1fa69
--- /dev/null
+++ b/.github/actions/to-integration-tests/server.key.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+GmwfqLWDBpxKXfwCxKohA3Y/Leey9sYj
+-----END PRIVATE KEY-----
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2e2015e810..6d366dae38 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@ The format is based on [Keep a 
Changelog](http://keepachangelog.com/en/1.0.0/).
 - [#7055](https://github.com/apache/trafficcontrol/issues/7055) *Traffic 
Portal* Made `Clear Table Filters` option visible to the user.
 - [#7024](https://github.com/apache/trafficcontrol/pull/7024) *Traffic 
Monitor* Added logging for `ipv4Availability` and `ipv6Availability` in TM.
 - [#7063](https://github.com/apache/trafficcontrol/pull/7063) *Traffic Ops* 
Added API version 5.0 (IN DEVELOPMENT)
+- [#7645](https://github.com/apache/trafficcontrol/pull/7645) *Traffic Ops* 
Added a client method to be able to login with certificates.
 - [#2101](https://github.com/apache/trafficcontrol/issues/2101) *Traffic 
Portal* Added the ability to tell if a Delivery Service is the target of 
another steering DS.
 - [#6021](https://github.com/apache/trafficcontrol/issues/6021) *Traffic 
Portal* Added the ability to view a change logs message in it's entirety by 
clicking on it.
 - [#7078](https://github.com/apache/trafficcontrol/issues/7078) *Traffic Ops, 
Traffic Portal* Added ability to assign multiple server capabilities to a 
server.
diff --git a/traffic_ops/testing/api/v5/client-intermediate-chain.crt.pem 
b/traffic_ops/testing/api/v5/client-intermediate-chain.crt.pem
new file mode 100644
index 0000000000..609fafdab1
--- /dev/null
+++ b/traffic_ops/testing/api/v5/client-intermediate-chain.crt.pem
@@ -0,0 +1,67 @@
+-----BEGIN CERTIFICATE-----
+MIIFtTCCA52gAwIBAgIIfnM8Us4oJWgwDQYJKoZIhvcNAQELBQAwbTELMAkGA1UE
+BhMCVVMxETAPBgNVBAgTCENvbG9yYWRvMQ8wDQYDVQQHEwZEZW52ZXIxDzANBgNV
+BAoTBkFwYWNoZTEMMAoGA1UECxMDQVRDMRswGQYDVQQDExJpbnRlcm1lZGlhdGUu
+bG9jYWwwHhcNMjMwNzEyMjMwMzM3WhcNMjQwNzEyMjMwMzM3WjB+MQswCQYDVQQG
+EwJVUzERMA8GA1UECBMIQ29sb3JhZG8xDzANBgNVBAcTBkRlbnZlcjEPMA0GA1UE
+ChMGQXBhY2hlMQwwCgYDVQQLEwNBVEMxFTATBgNVBAMTDGNsaWVudC5sb2NhbDEV
+MBMGCgmSJomT8ixkAQETBWFkbWluMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+CgKCAgEAv0i7fUz7DzhSRVL9i7e336vKPAuoByCqGtRnC5977mMUtyAVqLJhb4Ae
+E6JE/d5F2fv7FM4MaC37cAW1s01oqlyp8mNUwWs7seXWJ3+dFmkIvDL8GWUGJwdz
+jOND8BqfVCg2YRApM+Q4DLBjpJNi3Jho7HNH5DafZkYwZ5w7EUPRHti0d8HVaiSp
+Ms6bK1lhIXpcanAE+DRIDhMWQSs6kUU9y93WFk/K1SFtK4WsLUbJRDZGjHh7zsGR
+gMrFqaelacgg1ztJpPtx7U5XfQZ2o5IMkc/Z1E1y/W8xhds/5ubqedSLG6IZ1u9i
+SqqcvJC5N+jnkFqyVlB8qeiqrQYThZt8xG/7dgspnJMo9WB793Y/UtgjIDvyogM7
+ZXFJ5R8Hlx+0KFs//nxen5AkNVis87yzxQmR1PLmD79BXz6yQNbwaOb+yn50xhOe
+iYIlo+v2YakVyqukkcccC9c3RX+64gO9TBjE/SMf10zGQQ98G9pql8Ay/GLvTSjt
+B6MxxmS2kykRc13xDbiUkSTksQBt/XhZqBDLQ0+89/JWGEhxe9C+TfOSCjmXuPxd
+FlGJkyWnNoixnMkheVN++iYNKTX9LdoPi4/qNCLhiMKyV/0WnLeN4DFaYOpS6BGf
+oBXxtCyXwZXbYRgun/ys57PjsQfpr0B0wa3i3mMTwmfaSOULdy0CAwEAAaNIMEYw
+DgYDVR0PAQH/BAQDAgOIMBMGA1UdJQQMMAoGCCsGAQUFBwMCMB8GA1UdIwQYMBaA
+FKxRMLi2S3VfGaBIzZzU1f1IMcihMA0GCSqGSIb3DQEBCwUAA4ICAQCLeApvePB9
+g1Npl62am/KPioPcbKlGP8E2MpnRtsaA9ctDoRwaxFtIGtKtJlnWMbbq3mkliVBM
+MqFBQbq2XfJ2lrkjuZO76GnmsDakgISkpqoy/AROSrf0D+rcfqFUogGBIFK5965m
+6OynMSTyrPmMGyVSrA5jlzMofjHmhQ7qZ3nBEfi/BbAv6kBwp5s405pLF3Pg3C1I
+ftDaAJxca+tr++/eMNzgL0p75/942X00VBgZN9z9viIOXhSmupApKLJ3e89ku7Zs
+7+IDmrie75D7PC5J0Nlu6PzLXNZ+4GHFO0R0PBZFWWKv6SmUjEmSTYw3BJF9JPKG
+e2nrW+RW6wOQoLNS12j8Evx7HfGoBSJeR6pJWhRlJOHyIbjTS9vTmbw20fZy1bBQ
+50GW2mXdilS4Y/5A3xlCbEbUb9bTsI3gG6NH/BqfykhKmSNJaZDSDFNCzc2Fydwi
+wqAaoIRcJL+xe4I1IagGsXeBrWJf2zLC37B0YhkeMJi6xbYW4EwcjwIEvOQERSQM
+V6EtgA5OB/fAHaOwnT/LtOqFGDKS7mRQpo+SRqGc9JZ/US/U+jT8/I322co6pQ7i
+OmEi5dWNZPUSkFdxME1aCpz85kacsHnP+WLg6cLoiRRPpWHq2S0UJ0jUYNdtloVW
+IfQRNCtV+TFe1Bn+S4aTkm/tAr5Tgw3IuA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+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
+xOru9bnt/khEvzHOf6tc
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/traffic_ops/testing/api/v5/client.key.pem 
b/traffic_ops/testing/api/v5/client.key.pem
new file mode 100644
index 0000000000..79701b131a
--- /dev/null
+++ b/traffic_ops/testing/api/v5/client.key.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
++umc6cKeZTKflUaR2/XBqo24WjqEgQ==
+-----END PRIVATE KEY-----
\ No newline at end of file
diff --git a/traffic_ops/testing/api/v5/session_test.go 
b/traffic_ops/testing/api/v5/session_test.go
index 76e3626a45..8af489bf0b 100644
--- a/traffic_ops/testing/api/v5/session_test.go
+++ b/traffic_ops/testing/api/v5/session_test.go
@@ -16,6 +16,7 @@ package v5
 */
 
 import (
+       "testing"
        "time"
 
        client "github.com/apache/trafficcontrol/traffic_ops/v5-client"
@@ -48,8 +49,21 @@ func TeardownSession(toReqTimeout time.Duration, toURL 
string, toUser string, to
 func SwitchSession(toReqTimeout time.Duration, toURL string, toOldUser string, 
toOldPass string, toNewUser string, toNewPass string) error {
        err := TeardownSession(toReqTimeout, toURL, toOldUser, toOldPass)
 
-       // intentially skip errors so that we can continue with setup in the 
event of a 403
+       // intentionally skip errors so that we can continue with setup in the 
event of a 403
 
        err = SetupSession(toReqTimeout, toURL, toNewUser, toNewPass)
        return err
 }
+
+func TestLoginWithCert(t *testing.T) {
+       session, _, err := client.LoginWithCert(Config.TrafficOps.URL, true, 
time.Second*60,
+               "./client-intermediate-chain.crt.pem",
+               "./client.key.pem", "")
+
+       if err != nil {
+               t.Fatalf("expected no error while logging in with cert, but got 
%v", err)
+       }
+       if session == nil {
+               t.Fatalf("expected a valid session, but got nothing")
+       }
+}
diff --git a/traffic_ops/toclientlib/toclientlib.go 
b/traffic_ops/toclientlib/toclientlib.go
index ffc17829b1..6208daa60e 100644
--- a/traffic_ops/toclientlib/toclientlib.go
+++ b/traffic_ops/toclientlib/toclientlib.go
@@ -319,6 +319,53 @@ func (to *TOClient) logout() (net.Addr, error) {
        return remoteAddr, nil
 }
 
+// LoginWithCert returns an authenticated TOClient.
+//
+// Start with
+//
+//     toURL := "https://trafficops.example";
+//     apiVers := []string{"3.0", "3.1"}
+//     to := LoginWithClient(toURL, true, "certfile", "keyFile", "myapp/1.0", 
DefaultTimeout, apiVers)
+//
+// subsequent calls like to.GetData("datadeliveryservice") will be 
authenticated.
+//
+// Returns the logged in client, the remote IP address of Traffic Ops to which
+// the given URL was resolved and used to authenticate, and any error that
+// occurred. If the error is not nil, the remote address may or may not be nil,
+// depending on whether the error occurred before the login request.
+//
+// apiVersions is the list of API versions supported in this client. This
+// should generally be provided by the client package wrapping this package.
+func LoginWithCert(
+       toURL string,
+       insecure bool,
+       requestTimeout time.Duration,
+       certFile string,
+       keyFile string,
+       userAgent string,
+       apiVersions []string,
+) (*TOClient, net.Addr, error) {
+       cert, err := tls.LoadX509KeyPair(certFile, keyFile)
+       if err != nil {
+               return nil, nil, err
+       }
+       to := NewClient("", "", toURL, userAgent, &http.Client{
+               Timeout: requestTimeout,
+               Transport: &http.Transport{
+                       TLSClientConfig: &tls.Config{
+                               Certificates:       []tls.Certificate{cert},
+                               InsecureSkipVerify: insecure,
+                       },
+               },
+       }, apiVersions)
+
+       reqInf, err := to.login()
+       if err != nil {
+               return nil, reqInf.RemoteAddr, errors.New("logging in: " + 
err.Error())
+       }
+       return to, reqInf.RemoteAddr, nil
+}
+
 // LoginWithAgent returns an authenticated TOClient.
 //
 // Start with
diff --git a/traffic_ops/traffic_ops_golang/auth/certificate.go 
b/traffic_ops/traffic_ops_golang/auth/certificate.go
index f2f1ea725a..50fd78f0aa 100644
--- a/traffic_ops/traffic_ops_golang/auth/certificate.go
+++ b/traffic_ops/traffic_ops_golang/auth/certificate.go
@@ -32,25 +32,25 @@ import (
        "github.com/apache/trafficcontrol/lib/go-rfc/ldap"
 )
 
-// ParseCertificate takes a http.Request, pulls the (optionally) provided 
client TLS
+// VerifyClientCertificate takes a http.Request, pulls the (optionally) 
provided client TLS
 // certificates and attempts to verify them against the directory of provided 
Root CA
 // certificates. The Root CA certificates can be different than those utilized 
by the
 // http.Server. Returns an error if the verification process fails
-func VerifyClientCertificate(r *http.Request, rootCertsDirPath string) error {
+func VerifyClientCertificate(r *http.Request, rootCertsDirPath string, 
insecureSkipVerify bool) error {
        // TODO: Parse client headers as alternative to TLS in the request
 
        if err := loadRootCerts(rootCertsDirPath); err != nil {
                return fmt.Errorf("failed to load root certificates")
        }
 
-       if err := verifyClientRootChain(r.TLS.PeerCertificates); err != nil {
+       if err := verifyClientRootChain(r.TLS.PeerCertificates, 
insecureSkipVerify); err != nil {
                return fmt.Errorf("failed to verify client to root certificate 
chain")
        }
 
        return nil
 }
 
-func verifyClientRootChain(clientChain []*x509.Certificate) error {
+func verifyClientRootChain(clientChain []*x509.Certificate, insecureSkipVerify 
bool) error {
        if len(clientChain) == 0 {
                return fmt.Errorf("empty client chain")
        }
@@ -71,6 +71,9 @@ func verifyClientRootChain(clientChain []*x509.Certificate) 
error {
        }
        _, err := clientChain[0].Verify(opts)
        if err != nil {
+               if insecureSkipVerify {
+                       return nil
+               }
                return fmt.Errorf("failed to verify client cert chain. err: 
%w", err)
        }
        return nil
diff --git a/traffic_ops/traffic_ops_golang/auth/certificate_test.go 
b/traffic_ops/traffic_ops_golang/auth/certificate_test.go
index ba1fab484a..9a598cd9a8 100644
--- a/traffic_ops/traffic_ops_golang/auth/certificate_test.go
+++ b/traffic_ops/traffic_ops_golang/auth/certificate_test.go
@@ -63,7 +63,7 @@ func TestVerifyClientCertificate_Success(t *testing.T) {
        connState.PeerCertificates = append(connState.PeerCertificates, 
intermediateCert)
        req.TLS = connState
 
-       err = VerifyClientCertificate(req, "root/pool/created/above")
+       err = VerifyClientCertificate(req, "root/pool/created/above", false)
        if err != nil {
                t.Fatalf("error failed to verify client certificate: %s", err)
        }
@@ -96,7 +96,7 @@ func TestVerifyClientCertificate_NoIntermediate_Fail(t 
*testing.T) {
        connState.PeerCertificates = append(connState.PeerCertificates, 
clientCert)
        req.TLS = connState
 
-       err = VerifyClientCertificate(req, "root/pool/created/above")
+       err = VerifyClientCertificate(req, "root/pool/created/above", false)
        if err == nil {
                t.Fatalf("should have failed without intermediate certificate: 
%s", err)
        }
@@ -136,7 +136,7 @@ func TestVerifyClientChainSuccess(t *testing.T) {
                t.Fatalf("failed to extract x509 from PEM string for 
intermediateCert. err: %s", err)
        }
 
-       if err = verifyClientRootChain([]*x509.Certificate{clientCert, 
intermediateCert}); err != nil {
+       if err = verifyClientRootChain([]*x509.Certificate{clientCert, 
intermediateCert}, false); err != nil {
                t.Fatalf("failed to verify certificate chain with valid certs. 
err: %s", err)
        }
 }
@@ -153,7 +153,7 @@ func TestVerifyClientChain_EmptyClient_Fail(t *testing.T) {
        rootPool = x509.NewCertPool()
        rootPool.AddCert(rootCert)
 
-       if err = verifyClientRootChain([]*x509.Certificate{}); err == nil {
+       if err = verifyClientRootChain([]*x509.Certificate{}, false); err == 
nil {
                t.Fatalf("failed to verify certificate chain with valid certs. 
err: %s", err)
        }
 }
@@ -167,7 +167,7 @@ func TestVerifyClientChain_EmptyRoot_Fail(t *testing.T) {
                t.Fatalf("failed to extract x509 from PEM string for 
clientCert. err: %s", err)
        }
 
-       if err = verifyClientRootChain([]*x509.Certificate{clientCert}); err == 
nil {
+       if err = verifyClientRootChain([]*x509.Certificate{clientCert}, false); 
err == nil {
                t.Fatalf("failed to verify certificate chain with valid certs. 
err: %s", err)
        }
 }
@@ -191,7 +191,7 @@ func TestVerifyClientChain_WrongCertKeyUsage_Fail(t 
*testing.T) {
                t.Fatalf("failed to extract x509 from PEM string for 
serverCert. err: %s", err)
        }
 
-       if err = verifyClientRootChain([]*x509.Certificate{serverCert}); err == 
nil {
+       if err = verifyClientRootChain([]*x509.Certificate{serverCert}, false); 
err == nil {
                t.Fatalf("failed to verify certificate chain with valid certs. 
err: %s", err)
        }
 }
diff --git a/traffic_ops/traffic_ops_golang/login/login.go 
b/traffic_ops/traffic_ops_golang/login/login.go
index ada7dfbe2d..2689109426 100644
--- a/traffic_ops/traffic_ops_golang/login/login.go
+++ b/traffic_ops/traffic_ops_golang/login/login.go
@@ -120,7 +120,7 @@ func clientCertAuthentication(w http.ResponseWriter, r 
*http.Request, db *sqlx.D
        }
 
        // Perform certificate verification to ensure it is valid against Root 
CAs
-       err := auth.VerifyClientCertificate(r, cfg.ClientCertAuth.RootCertsDir)
+       err := auth.VerifyClientCertificate(r, cfg.ClientCertAuth.RootCertsDir, 
cfg.Insecure)
        if err != nil {
                log.Warnf("client cert auth: error attempting to verify client 
provided TLS certificate. err: %s\n", err)
                return false
diff --git a/traffic_ops/v5-client/session.go b/traffic_ops/v5-client/session.go
index 1072281660..3408cb8580 100644
--- a/traffic_ops/v5-client/session.go
+++ b/traffic_ops/v5-client/session.go
@@ -79,6 +79,21 @@ func NewSession(user, password, url, userAgent string, 
client *http.Client, useC
        }
 }
 
+// LoginWithCert creates a new authenticated session with a Traffic Ops
+// server, and gives the client the ability to log in using certificates.
+//
+// Returns the logged in client, the remote address of Traffic Ops which was
+// translated and used to log in, and any error that occurred along the way. If
+// the error is not nil, the remote address may or may not be nil, depending on
+// whether the error occurred before the login request.
+func LoginWithCert(toURL string, insecure bool, requestTimeout time.Duration, 
certFile, keyFile, userAgent string) (*Session, net.Addr, error) {
+       cl, ip, err := toclientlib.LoginWithCert(toURL, insecure, 
requestTimeout, certFile, keyFile, userAgent, apiVersions())
+       if err != nil {
+               return nil, nil, err
+       }
+       return &Session{TOClient: *cl}, ip, err
+}
+
 // LoginWithAgent creates a new authenticated session with a Traffic Ops
 // server. The session cookie should be set automatically in the returned
 // Session, so that subsequent calls are properly authenticated without further


Reply via email to