diff --git a/CHANGES/pulp_certguard/5054.misc b/CHANGES/pulp_certguard/5054.misc new file mode 100644 index 00000000000..d0c100a1739 --- /dev/null +++ b/CHANGES/pulp_certguard/5054.misc @@ -0,0 +1 @@ +Converted docs to use new documentation-stack. diff --git a/pulp_certguard/staging_docs/admin/learn/reverse_proxy_config.md b/pulp_certguard/staging_docs/admin/learn/reverse_proxy_config.md new file mode 100644 index 00000000000..1c5bdabc71a --- /dev/null +++ b/pulp_certguard/staging_docs/admin/learn/reverse_proxy_config.md @@ -0,0 +1,64 @@ +# Reverse Proxy Config + +The client certificate submitted terminates [Transport Layer Security](https://en.wikipedia.org/wiki/Transport_Layer_Security) (TLS) at the reverse proxy. The reverse proxy must do two things to +correctly pass the client certificate to the `pulpcore-content` app. + +1. Forward the client's TLS certificate as the `X-CLIENT-CERT`. +2. The `X-CLIENT-CERT` needs to be urlencoded. + +## Nginx Config Example + +To configure Nginx to accept a client cert, and have it forward the urlencoded cert: + +1. Enable the checking of a client cert with the [ssl_verify_client directive](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_verify_client). + +2. Configure the `X-CLIENT-CERT` header to be urlencoded and forwarded. To avoid a client + falsifying the header, first unset it. It forwards the [\$ssl_client_escaped_cert](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#var_ssl_client_escaped_cert) variable + which is the urlencoded client cert: + + ``` + proxy_set_header X-CLIENT-CERT $ssl_client_escaped_cert; + ``` + +## Apache 2.4.10+ Config Example + +To configure Apache to accept a client cert, urlencode it, and forward it you will need to: + +1. Enable the checking of a client cert with the [SSLVerifyClient directive](https://httpd.apache.org/docs/current/mod/mod_ssl.html#sslverifyclient). + +2. Enable the client certificate to be available as an environment variable with: + + ``` + SSLOptions +ExportCertData + ``` + +3. Configure the `X-CLIENT-CERT` header to be urlencoded and forwarded. To avoid a client + falsifying the header, first unset it. Then use [mod_rewrite](https://httpd.apache.org/docs/current/mod/mod_rewrite.html) to urlencode the [SSL_CLIENT_CERT](https://httpd.apache.org/docs/2.4/mod/mod_ssl.html) environment variable as follows: + + ``` + RequestHeader unset X-CLIENT-CERT + RequestHeader set X-CLIENT-CERT "expr=%{escape:%{SSL_CLIENT_CERT}s}" + ``` + +## Apache \< 2.4.10 Config Example + +Apache versions earlier than 2.4.10 are not able to urlencode the client certificate. +`pulp-certguard` tries to detect this situation and work anyway. + +In this case, to configure Apache to accept a client cert and forward it you will need to: + +1. Enable the checking of a client cert with the [SSLVerifyClient directive](https://httpd.apache.org/docs/current/mod/mod_ssl.html#sslverifyclient). + +2. Enable the client certificate to be available as an environment variable with: + + ``` + SSLOptions +ExportCertData + ``` + +3. Configure the `X-CLIENT-CERT` header to be forwarded. To avoid a client falsifying the header, + first unset it, then forward the data in the [SSL_CLIENT_CERT](https://httpd.apache.org/docs/2.4/mod/mod_ssl.html) environment variable as follows: + + ``` + RequestHeader unset X-CLIENT-CERT + RequestHeader set X-CLIENT-CERT "%{SSL_CLIENT_CERT}s" + ``` diff --git a/pulp_certguard/staging_docs/user/learn/debugging.md b/pulp_certguard/staging_docs/user/learn/debugging.md new file mode 100644 index 00000000000..49b633ab201 --- /dev/null +++ b/pulp_certguard/staging_docs/user/learn/debugging.md @@ -0,0 +1,81 @@ +# Debugging + +`pulp-certguard` contains debug statements which show the raw, received value of the +`X-CLIENT-CERT` header. This can be very valuable when debugging where the problem is in the chain +of certificates from the: + +``` +client <--> reverse proxy <--> pulp-certguard +``` + +## Enabling Debugging + +Debugging is most easily enabled by adding the following line to your settings file, which is +by default located at `/etc/pulp/settings.py`: + +``` +LOGGING = {"dynaconf_merge": True, "loggers": {'': {'handlers': ['console'], 'level': 'DEBUG'}}} +``` + +After restarting your server-side services and making a request that sets the `X-CLIENT-CERT` +header, you should see a log message for each request where pulp-certguard is receiving a +`X-CLIENT-CERT` header. + +## Using Logging Info + +If you make a request but do not see a log message, you could have one of the following problems: + +1. Debug logging is not enabled or applied. Check your `LOGGING` config. +2. The client is not requesting content from a Distribution protected with `pulp-certguard`. Check + your `Distribution` configuration. +3. The reverse proxy isn't configured to pass along the `X-CLIENT-CERT` config correctly. Check + your reverse proxy config against the example configs documented on this site. + +If you do see a log message, but it's still not working you could have one of the following +problems: + +1. The client isn't submitting the client certificate correctly to the reverse proxy. Ensure the + client is submitting a certificate and key via TLS to the reverse proxy. +2. The reverse proxy configuration is not correct. Compare your reverse proxy config against the + example configs documented on this site. + +## Checking Authorized URLs in RHSM Certificates + +The `rct cat-crt` command is useful for printing the detailed contents of RHSM certificates. This +is typically provided by the `subscription-manager` rpm on Centos, Fedora, and RHEL systems. + +Once installed you can show the contents of an RHSM cert like this example running on a +[test certificate](https://github.com/pulp/pulp-certguard/blob/master/pulp_certguard/tests/functional/artifacts/rhsm/v3/4260035510644027985.pem): + +``` +$ rct cat-cert 4260035510644027985.pem + ++-------------------------------------------+ + Entitlement Certificate ++-------------------------------------------+ + +Certificate: + Path: v3/4260035510644027985.pem + Version: 3.4 + Serial: 4260035510644027985 + Start Date: 2020-03-05 19:50:59+00:00 + End Date: 2048-06-01 00:00:00+00:00 + Pool ID: Not Available + +Subject: + CN: d3c3ff52c107457dbd3a0c28a345754a + O: Default_Organization + +Issuer: + C: US + CN: sat-6-6-qa-rhel7.windhelm.example.com + L: Raleigh + O: Katello + OU: SomeOrgUnit + ST: North Carolina + + + +Authorized Content URLs: + /Default_Organization/Library/custom/foo/foo +``` diff --git a/pulp_certguard/staging_docs/user/tutorials/overview.md b/pulp_certguard/staging_docs/user/tutorials/overview.md new file mode 100644 index 00000000000..077cb720891 --- /dev/null +++ b/pulp_certguard/staging_docs/user/tutorials/overview.md @@ -0,0 +1,98 @@ +# Overview + +The `pulp_certguard` plugin for [pulpcore 3.0+](https://pypi.org/project/pulpcore/) can cause +Pulp to refuse to serve content, e.g. rpms, Ansible Collections, etc, unless clients present a +valid certificate when they fetch content. + +## Example + +Company Foo only wants to serve rpms to customers who have paid, and they use Pulp 3.0+ to store +rpms for their customers. When a customer pays through, e.g. June 30, 2023, Company Foo generates +the customer a certificate signed by the certificate authority of Company Foo with an expiration +date of June 30, 2023. Company Foo also does the following: + +1. Creates either a X.509 or RHSM Cert Guard offered by this plugin, configured with the Certificate + Authority Certificate used to sign the customer certificates. +2. Configures one or more Pulp Distributions which serving rpm repositories to be protected by the + X.509 or RHSM Cert Guard they created. + +## Example Usage + +The presentation of the client certificate should be done using TLS so the client can prove it not +only has the certificate but also the key for that certificate. This TLS connection terminates at +the reverse proxy, e.g. Nginx or Apache, and the client certificate is then forwarded to the +`pulpcore-content` app for Authorization checking. The forwarded certificate is expected to be +delivered to `pulpcore-content` as a urlencoded version of the client certificate stored in the +`X-CLIENT-CERT` HTTP header. Here's a diagram of the call flow: + +client \<-- TLS --> Nginx \<-- X-CLIENT-CERT header --> pulpcore-content + +!!! note + The `X-CLIENT-CERT` header needs to be urlencoded because newline characters present in valid + client certificates are not allowed in headers. + + +## Authorization + +Any client presenting an X.509 or RHSM based certificate at request time will be authorized if and +only if the client cert is unexpired and signed by stored Certificate Authority Certificate stored +on the Certguard when it was created. + +This allows for a use case where multiple Certificate Authority Certificates can be used to allow +some Pulp Distributions to be give access to some clients but not others depending on which CA Cert +signed the client certificate. While that is useful, it can become unwieldy to have to also manage +many CA Certificates. To resolve this an optional, additional authorization mechanism is available +where paths to Pulp Distributions are contained in the certificate itself. + +## RHSM Certificate Path Based Authorization + +!!! warning + At this time only the RHSM Cert Guard provides path based authorization. + + +RHSM Certificates allow for the embedding of Distribution.base_path within them. If they are present +in a client certificate, in addition to the authorization requirements from above, the client is +granted access if and only if the requested path is a subpath of a path contained in the client +certificate. + +For example if the RHSM Certificate contains Authorized URL: + +``` +Authorized Content URLs: + /some/path/foo +``` + +Pulp would serve content from any protected Distribution with the following +`Distribution.base_path` values: + +``` +some +some/path +some/path/foo +``` + +Pulp would not serve content from a protected Distribution with the following +`Distribution.base_path` values: + +``` +another/path +some/path/bar +``` + +!!! note + The `pulpcore-content` app typically serves `Distribution.base_path` at a url starting with + `/pulp/content/`. When performing path checking only the `Distribution.base_path` portion of + the URL is checked. + +`Distribution.base_bath` uses partial paths so it does *not* start with slash or end with one. +The Authorized URLs on the other hand do start with a slash. The RHSM path authorization check +code in `pulp-certguard` prepends a slash in front of the `Distribution.base_path`. This +allows a `Distribution.base_path` of `some/path` to become `/some/path` which would match +in RHSM against a certificate with either `/some` or `/some/path`. + + +## RHSM vs X.509 Cert Guards + +pulp_certguard has two types of Cert Guards, the X509CertGuard and the RHSMCertGuard. Both are X.509 +certificates, but the X509CertGuard can be made using common openssl tools, while RHSM Certificates +can only be made using [python-rhsm](https://pypi.org/project/rhsm/). diff --git a/pulp_certguard/staging_docs/user/tutorials/usage.md b/pulp_certguard/staging_docs/user/tutorials/usage.md new file mode 100644 index 00000000000..d284e035722 --- /dev/null +++ b/pulp_certguard/staging_docs/user/tutorials/usage.md @@ -0,0 +1,149 @@ +# Usage + +To meaningfully use pulp-certguard you should already have a Pulp Distribution that requires +authorization and ideally it should have content in it. These examples assume you have a [pulp_file](site:/pulp_file/) +`FileRepository` with at least one `RepositoryVersion` with content in it. Also you'll need a `FileDistribution` serving that +`RepositoryVersion`. + +The pulp-certguard examples should be straightforward to port to protect another distribution type. + +### Create content to be protected + +This step is about creating some data to test with. The significant thing for pulp-certguard is +having a repository to protect and having some content in that repository to test against. +```bash +echo "Creating FileRemote..." +pulp file remote create \ + --name certguard-remote \ + --url "https://fixtures.pulpproject.org/file/PULP_MANIFEST" \ + --policy on_demand +echo "Creating FileRepository..." +pulp file repository create \ + --name certguard-repository \ + --remote certguard-remote \ + --autopublish +echo "Sync repository..." +pulp file repository sync \ + --name certguard-repository +echo "Distribute the respoitory's content..." +pulp file distribution create \ + --name certguard-distribution \ + --repository file:file:certguard-repository \ + --base-path file/certguard-repository +``` + +## X509 CertGuard + +### Create a content guard + +This example assumes that `./ca.pem` is a PEM encoded Certificate Authority (CA) certificate. Each +X509 Content Guard needs a name so for this example we'll use `myguard`. + +=== "Create X509 ContentGuard" + + ```bash + pulp content-guard x509 create \ + --name my-509-guard \ + --ca-certificate=@./ca.pem + ``` + +=== "Create RHSM ContentGuard" + + ```bash + pulp content-guard rhsm create \ + --name my-rhsm-guard \ + --ca-certificate=@./ca.pem + ``` + +=== "X509 Output" + + ```json + { + "pulp_href": "/pulp/api/v3/contentguards/certguard/x509/018dbdce-83c8-7602-ae8f-e8d5262d3cb8/", + "pulp_created": "2024-02-18T20:00:44.489636Z", + "name": "my-509-guard", + "description": null, + "ca_certificate": "-----BEGIN CERTIFICATE-----\n...-----END CERTIFICATE-----" + } + ``` + +=== "RHSM Output" + + ```json + { + "pulp_href": "/pulp/api/v3/contentguards/certguard/rhsm/018dbddd-1894-7658-ab8f-1f33b544f2fc/", + "pulp_created": "2024-02-18T20:16:40.085402Z", + "name": "my-rhsm-guard", + "description": null, + "ca_certificate": "-----BEGIN CERTIFICATE-----\n...-----END CERTIFICATE-----" + } + ``` + +### Protect the Distribution with the X509CertGuard + +=== "Associate a ContentGuard to a Repository" + + ```bash + pulp file distribution update \ + --name certguard-distribution \ + --content-guard certguard:x509:my-509-guard + ``` + +=== "Output" + + ```console + Started background task /pulp/api/v3/tasks/018dbdd6-ec6e-7cd9-a49d-1c1a2e55725f/ + Done. + ``` + +### Download `protected` content + +#### X509 and RHSM Certguards + +The following example assume the client will connect to the reverse proxy using TLS with the +following: + +- The PEM encoded client certificate is stored at `~/client.pem` which is signed by the CA stored + on the X509CertGuard. +- The corresponding PEM encoded private key at `~/key.pem`. + +It attempts to download the `1.iso` file from the FileDistribution at the path +`/pulp/content/somepath/` Note the `somepath` part of this is from the `base_url` of the +Distribution you are testing against. + +For example with httpie you can submit the client cert and key via TLS using: + +`$ http --cert ~/client.pem --cert-key ~/key.pem https://localhost/pulp/content/somepath/test.iso` + +This is expected to yield binary data with a response like: + +```console +HTTP/1.1 200 OK +Accept-Ranges: bytes +Connection: keep-alive +Content-Length: 3145728 +Content-Type: application/octet-stream +Date: Tue, 21 Apr 2020 20:35:11 GMT +Last-Modified: Tue, 21 Apr 2020 19:23:06 GMT +Server: nginx/1.16.1 + + + ++-----------------------------------------+ +| NOTE: binary data not shown in terminal | ++-----------------------------------------+ +``` + +## RHSM-CertGuard-specific rules + +!!! note + To use the `RHSMCertGuard` you have to manually install the [rhsm Python module](https://pypi.org/project/rhsm/) which provides RHSM certificate parsing on the pulp server. + It requires some system level dependencies, e.g. OpenSSL libraries, which are not the same on + all operating operating systems. `rhsm` from PyPI not being cross-distro is why this requires + manual installation. + + +If the RHSM client cert contains entitlement paths, **they must match the full path to the +Distribution** the client is fetching from. In this example that is `/pulp/content/somepath/`. + + diff --git a/pulp_certguard/staging_docs/user/tutorials/yum-howto.md b/pulp_certguard/staging_docs/user/tutorials/yum-howto.md new file mode 100644 index 00000000000..7d6406c02ea --- /dev/null +++ b/pulp_certguard/staging_docs/user/tutorials/yum-howto.md @@ -0,0 +1,178 @@ +# Configure yum/dnf + +To enable yum/dnf to access content protected by a content-guard, use the following as a guide. These are +essentially all you need to end up with a protected rpm repo. + +Working through these guides will help to make sense of +all the steps and how they relate to each other. + + + + + + + +## easy_rsa setup + +Once you get the easy_rsa rpm installed, copy the skeleton of scripts to where you want your cert infra to live. +The quickstart guide is easy to follow. + +If you keep everything in a local git repo, you can do reverts/diffs while learning new things. + +```bash +cp -r /usr/share/easy-rsa . +cd easy-rsa +git init +git add * +git commit -m 'pristine start' -a +cd 3 +./easyrsa init-pki + ./easyrsa build-ca +# now convert your crt to a pem +# you will use this later as a content guard in pulp +cd pki +openssl x509 -in ca.crt -out ca.pem -outform PEM +cd .. +# now generate a client cert +# you're going to do this on the server with the ca. +# the guide recommends not doing this, I keep everything in one place. +# The reason for this, is that pulp only will need a signed pem. No key, not csr, nothing else. +# So, I will generate a yum-client cert and sign it here. Then I'll copy the pem to the client machines. +# later, this will be automated via ansible. + +./easyrsa gen-req yum-client +# now sign it so its trusted +./easyrsa sign-req client yum-client +# now convert it to pem format +openssl x509 -in pki/issued/yum-client.crt -out pki/issued/yum-client.pem -outform PEM +``` + +## pulp setup + +NB: +The single biggest tip here is this: + +When a rest call returns a task id (uuid), you need to poll for +it. The async tasks don't log to disk. Errors will show up in the task +details. + +I had a ton of self inflicted problems due to doing this on a CIS +hardened image where the default umask leads to non-shared +directories/files when created (default 750 instead of 755 dirs, etc). +All the permission denied errors showed up fine in the tasks, but i +didn't immediately know to check there and wasted some time. + +```bash +# this is all in the pulp workflow as linked to above. +# create repo +http POST http://localhost:24817/pulp/api/v3/repositories/ name=boomi-epel + +# Lookup the thing we just made +export REPO_HREF=$(http http://localhost:24817/pulp/api/v3/repositories/ | jq -r '.results[] | select(.name == "boomi-epel") | .pulp_href') + +# create new remote +http POST http://localhost:24817/pulp/api/v3/remotes/rpm/rpm/ name='boomi-epel-remote' url='file:///usr/local/lib/pulp/staging/epel/' policy='immediate' + +export REMOTE_HREF=$(http :24817/pulp/api/v3/remotes/rpm/rpm/ | jq -r '.results[] | select(.name == "boomi-epel-remote") | .pulp_href') + +# Sync the repo +http POST http:/localhost:24817${REMOTE_HREF}sync/ repository=$REPO_HREF +# { +# "task": "/pulp/api/v3/tasks/abb42ba7-77f9-49c4-af05-3af407ab8596/" +# } + +# Inspect the new thingy.... +http GET http://localhost:24817${REPO_HREF}versions/1/ + +# Create a publication +http POST http://localhost:24817/pulp/api/v3/publications/rpm/rpm/ repository=$REPO_HREF + +# get repo-version (display/informational) +export PUBLICATION_HREF=$(http :24817/pulp/api/v3/publications/rpm/rpm/ | jq -r '.results[] | select(.repository_version|test("'$REPO_HREF'.")) | .pulp_href') + +http POST http://localhost:24817/pulp/api/v3/distributions/rpm/rpm/ name='boomi-epel-distro' base_path='boomi-epel' publication=$PUBLICATION_HREF + +#Follow the task progress here: +http GET http://localhost:24817/pulp/api/v3/tasks/ +http GET http://localhost:24817/pulp/api/v3/tasks/uuid-of-single-task-for-reasonable-responses + +# THe CONTENT_HOST setting is super important for this. Set it to something valid. + +# View all the published stats +http GET http://localhost:24817/pulp/api/v3/distributions/rpm/rpm/ + +# Get the repo metadata from the published end point on the yum side +http GET http://localhost:24816/pulp/content/boomi-epel-2/repodata/repomd.xml +``` + +## certguard setup + +The ca.pem and the yum-client.pem from above are needed for this part. + +Before you do this, make sure your non-protected repo works. I just +configured it on localhost and installed a sample rpm from my private +epel mirror. + +Alternatively, just check that yum makecache works. If it doesn't, +troubleshoot it til it does. Then we can add protection. + +```bash +# This is the essence of the guide for certguard linked to above. See that for better docs. + +http --form POST http://localhost:8000/pulp/api/v3/contentguards/certguard/x509/ name=boomi-ca ca_certificate@/var/lib/pulp-certs/easy-rsa/3/pki/ca.pem + +export GUARD_HREF=$(http localhost:24817/pulp/api/v3/contentguards/certguard/x509/?name=boomi-ca | jq -r '.results[0].pulp_href') + +# protect one +http PATCH http://localhost:24817/pulp/api/v3/distributions/rpm/rpm/4d9ef794-4af1-44ba-be5e-607defd396de/ content_guard=$GUARD_HREF +``` + +## yum setup + +Now that we're here, lets teach yum how to jam a signed cert in the +right http header when accessing one of our custom repos. + +```bash +# Show that the now protected repo wont let us in. +# confirm it denies the yum process +[root@ip-10-76-7-46 ~]# yum makecache +Loaded plugins: amazon-id, rhui-lb, search-disabled-repos +http://localhost:24816/pulp/content/boomi-epel-2/repodata/repomd.xml: [Errno 14] HTTP Error 403 - Forbidden +Trying other mirror. +To address this issue please refer to the below knowledge base article + +https://access.redhat.com/solutions/69319 + +If above article doesn't help to resolve this issue please open a ticket with Red Hat Support. + +Metadata Cache Created +[root@ip-10-76-7-46 ~]# + +cp $PATH_TO_EASYRSA/yum-client.pem /etc/boomi/yum.pem + +# Install my certguard plugin +copy to /usr/lib/yum-plugins + +# enable the plugin within yum +update /etc/yum/pluginconf.d/certguard.conf +[main] +enabled=1 + + +# kick the tires +[root@ip-10-76-7-46 yum-plugins]# yum makecache +Loaded plugins: amazon-id, certguard, rhui-lb, search-disabled-repos +boomi-epel | 3.5 kB 00:00:00 +(1/4): boomi-epel/updateinfo | 71 B 00:00:00 +(2/4): boomi-epel/filelists | 11 MB 00:00:00 +(3/4): boomi-epel/primary | 3.7 MB 00:00:00 +(4/4): boomi-epel/other | 2.3 MB 00:00:00 +boomi-epel 13215/13215 +boomi-epel 13215/13215 +boomi-epel 13215/13215 +Metadata Cache Created +[root@ip-10-76-7-46 yum-plugins]# + +NB: Make sure the cert paths are right. +Make sure the repo names begin with the right prefix +```