Skip to content

Conversation

@zajdee
Copy link
Contributor

@zajdee zajdee commented Nov 4, 2025

SUMMARY

This change implements the support of custom X509 extensions when generating a CSR.

Due to the need for an ASN.1 encoder, this change pulls a new component, pyasn1 (Github page), into the project. Encoder compatibility with pyasn1 versions back to 0.4.8 was tested. This extra Python component is only needed when custom_extensions are specified.

Because there can be various scenarios when generating the certificate request using Ansible, the values can be provided as raw data, raw Base64-encoded data, or as one of the following supported data types: int, real (float), bool, and str.

Empty extension value is not supported, and if specified, it either breaks the run, or (if allowed) such an extension definition is skipped.

Fixes #401

ISSUE TYPE
  • Feature Pull Request
COMPONENT NAME

The CSR generating backend, docs, and tests were modified.

ADDITIONAL INFORMATION

A potential use of this new feature is as follows:

- name: Generate mTLS CSR
  community.crypto.openssl_csr:
(...)
  custom_extensions:
      - oid: 1.3.6.1.4.1.34380.1.1.12
        value: "DE"
        critical: false
        skip_if_empty: true
      - oid: 1.3.6.1.4.1.34380.1.1.13
        value: ""
        critical: false
        skip_if_empty: true
      - oid: 1.3.6.1.4.1.34380.1.1.14
        value: 17
        value_type: int
        critical: false
      - oid: 1.3.6.1.4.1.34380.1.1.15
        value: 3.14
        value_type: real
        critical: false
      - oid: 1.3.6.1.4.1.34380.1.1.16
        value: true
        value_type: bool
        critical: false
      - oid: 1.3.6.1.4.1.34380.1.1.17
        value: false
        value_type: bool
        critical: false
      - oid: 1.3.6.1.4.1.34380.1.1.18
        value_raw: "DE"
        critical: false
        skip_if_empty: true
      - oid: 1.3.6.1.4.1.34380.1.1.19
        value_b64: "REU=" # DE
        critical: true
        skip_if_empty: true

@zajdee
Copy link
Contributor Author

zajdee commented Nov 4, 2025

Hello, this is my first PR to this project, so please accept my apologies if something breaks. For starters, I am not sure how to run tests locally - any hint is appreciated.

I have successfully generated CSRs with custom extensions by using this modified code, but there might be edge cases that I didn't hit.

The code currently doesn't check for any collision between the extensions added by the module and specified by the user, it doesn't even check if a custom extension (with the same OID) is defined multiple times by the user. The second part could be probably added easily.

@github-actions
Copy link

github-actions bot commented Nov 4, 2025

Docs Build 📝

Thank you for contribution!✨

The docs for this PR have been published here:
https://ansible-collections.github.io/community.crypto/pr/966

You can compare to the docs for the main branch here:
https://ansible-collections.github.io/community.crypto/branch/main

The docsite for this PR is also available for download as an artifact from this run:
https://github.com/ansible-collections/community.crypto/actions/runs/19132087252

File changes:

Click to see the diff comparison.

NOTE: only file modifications are shown here. New and deleted files are excluded.
See the file list and check the published docs to see those files.

diff --git a/home/runner/work/community.crypto/community.crypto/docsbuild/base/openssl_csr_module.html b/home/runner/work/community.crypto/community.crypto/docsbuild/head/openssl_csr_module.html
index 25beb80..c88b004 100644
--- a/home/runner/work/community.crypto/community.crypto/docsbuild/base/openssl_csr_module.html
+++ b/home/runner/work/community.crypto/community.crypto/docsbuild/head/openssl_csr_module.html
@@ -214,6 +214,7 @@ see <a class="reference internal" href="#ansible-collections-community-crypto-op
 <p>The below requirements are needed on the host that executes this module.</p>
 <ul class="simple">
 <li><p>cryptography &gt;= 3.3</p></li>
+<li><p>pyasn1 &gt;= 0.4.8 (only if <code class="ansible-option docutils literal notranslate"><strong><a class="reference internal" href="#ansible-collections-community-crypto-openssl-csr-module-parameter-custom-extensions"><span class="std std-ref"><span class="pre">custom_extensions</span></span></a></strong></code> are specified)</p></li>
 </ul>
 </section>
 <section id="parameters">
@@ -390,6 +391,83 @@ see <a class="reference internal" href="#ansible-collections-community-crypto-op
 </div></td>
 </tr>
 <tr class="row-odd"><td><div class="ansible-option-cell">
+<div class="ansibleOptionAnchor" id="parameter-custom_extensions"></div><p class="ansible-option-title" id="ansible-collections-community-crypto-openssl-csr-module-parameter-custom-extensions"><strong>custom_extensions</strong></p>
+<a class="ansibleOptionLink" href="#parameter-custom_extensions" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">list</span> / <span class="ansible-option-elements">elements=dictionary</span></p>
+<p><em class="ansible-option-versionadded">added in community.crypto 3.1.0</em></p>
+</div></td>
+<td><div class="ansible-option-cell"><p>Allows to specify one or multiple custom extensions.</p>
+<p>The extension value must not be empty, unless <code class="ansible-option docutils literal notranslate"><strong><a class="reference internal" href="#ansible-collections-community-crypto-openssl-csr-module-parameter-custom-extensions-skip-if-empty"><span class="std std-ref"><span class="pre">custom_extensions[].skip_if_empty</span></span></a></strong></code> is specified.</p>
+<p>In such case the extension will be skipped instead of throwing an error.</p>
+<p>Custom extensions require the <code class="ansible-value docutils literal notranslate"><span class="pre">pyasn1</span></code> Python package to be installed in the environment where the code is run.</p>
+</div></td>
+</tr>
+<tr class="row-even"><td><div class="ansible-option-indent"></div><div class="ansible-option-cell">
+<div class="ansibleOptionAnchor" id="parameter-custom_extensions/critical"></div><p class="ansible-option-title" id="ansible-collections-community-crypto-openssl-csr-module-parameter-custom-extensions-critical"><strong>critical</strong></p>
+<a class="ansibleOptionLink" href="#parameter-custom_extensions/critical" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">boolean</span></p>
+</div></td>
+<td><div class="ansible-option-indent-desc"></div><div class="ansible-option-cell"><p>Set the critical flag.</p>
+<p class="ansible-option-line"><strong class="ansible-option-choices">Choices:</strong></p>
+<ul class="simple">
+<li><p><code class="ansible-option-default-bold docutils literal notranslate"><strong><span class="pre">false</span></strong></code> <span class="ansible-option-choices-default-mark">← (default)</span></p></li>
+<li><p><code class="ansible-option-choices-entry docutils literal notranslate"><span class="pre">true</span></code></p></li>
+</ul>
+</div></td>
+</tr>
+<tr class="row-odd"><td><div class="ansible-option-indent"></div><div class="ansible-option-cell">
+<div class="ansibleOptionAnchor" id="parameter-custom_extensions/oid"></div><p class="ansible-option-title" id="ansible-collections-community-crypto-openssl-csr-module-parameter-custom-extensions-oid"><strong>oid</strong></p>
+<a class="ansibleOptionLink" href="#parameter-custom_extensions/oid" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">string</span></p>
+</div></td>
+<td><div class="ansible-option-indent-desc"></div><div class="ansible-option-cell"><p>The OID of the custom extension</p>
+<p>Example: <code class="ansible-value docutils literal notranslate"><span class="pre">1.3.6.1.4.1.34380.1.1.25</span></code>.</p>
+</div></td>
+</tr>
+<tr class="row-even"><td><div class="ansible-option-indent"></div><div class="ansible-option-cell">
+<div class="ansibleOptionAnchor" id="parameter-custom_extensions/skip_if_empty"></div><p class="ansible-option-title" id="ansible-collections-community-crypto-openssl-csr-module-parameter-custom-extensions-skip-if-empty"><strong>skip_if_empty</strong></p>
+<a class="ansibleOptionLink" href="#parameter-custom_extensions/skip_if_empty" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">boolean</span></p>
+</div></td>
+<td><div class="ansible-option-indent-desc"></div><div class="ansible-option-cell"><p>Allow empty value to be specified. In such case the extension will be skipped when processing.</p>
+<p class="ansible-option-line"><strong class="ansible-option-choices">Choices:</strong></p>
+<ul class="simple">
+<li><p><code class="ansible-option-default-bold docutils literal notranslate"><strong><span class="pre">false</span></strong></code> <span class="ansible-option-choices-default-mark">← (default)</span></p></li>
+<li><p><code class="ansible-option-choices-entry docutils literal notranslate"><span class="pre">true</span></code></p></li>
+</ul>
+</div></td>
+</tr>
+<tr class="row-odd"><td><div class="ansible-option-indent"></div><div class="ansible-option-cell">
+<div class="ansibleOptionAnchor" id="parameter-custom_extensions/value"></div><p class="ansible-option-title" id="ansible-collections-community-crypto-openssl-csr-module-parameter-custom-extensions-value"><strong>value</strong></p>
+<a class="ansibleOptionLink" href="#parameter-custom_extensions/value" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">string</span></p>
+</div></td>
+<td><div class="ansible-option-indent-desc"></div><div class="ansible-option-cell"><p>The value of the custom extension.</p>
+<p>Mutually exclusive with <code class="ansible-option docutils literal notranslate"><strong><a class="reference internal" href="#ansible-collections-community-crypto-openssl-csr-module-parameter-custom-extensions-value-raw"><span class="std std-ref"><span class="pre">custom_extensions[].value_raw</span></span></a></strong></code> and <code class="ansible-option docutils literal notranslate"><strong><a class="reference internal" href="#ansible-collections-community-crypto-openssl-csr-module-parameter-custom-extensions-value-b64"><span class="std std-ref"><span class="pre">custom_extensions[].value_b64</span></span></a></strong></code>.</p>
+</div></td>
+</tr>
+<tr class="row-even"><td><div class="ansible-option-indent"></div><div class="ansible-option-cell">
+<div class="ansibleOptionAnchor" id="parameter-custom_extensions/value_b64"></div><p class="ansible-option-title" id="ansible-collections-community-crypto-openssl-csr-module-parameter-custom-extensions-value-b64"><strong>value_b64</strong></p>
+<a class="ansibleOptionLink" href="#parameter-custom_extensions/value_b64" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">string</span></p>
+</div></td>
+<td><div class="ansible-option-indent-desc"></div><div class="ansible-option-cell"><p>The raw value of the custom extension, base64 encoded. Will be decoded, then copied to the extension as-is.</p>
+<p>Mutually exclusive with <code class="ansible-option docutils literal notranslate"><strong><a class="reference internal" href="#ansible-collections-community-crypto-openssl-csr-module-parameter-custom-extensions-value"><span class="std std-ref"><span class="pre">custom_extensions[].value</span></span></a></strong></code> and <code class="ansible-option docutils literal notranslate"><strong><a class="reference internal" href="#ansible-collections-community-crypto-openssl-csr-module-parameter-custom-extensions-value-raw"><span class="std std-ref"><span class="pre">custom_extensions[].value_raw</span></span></a></strong></code>.</p>
+</div></td>
+</tr>
+<tr class="row-odd"><td><div class="ansible-option-indent"></div><div class="ansible-option-cell">
+<div class="ansibleOptionAnchor" id="parameter-custom_extensions/value_raw"></div><p class="ansible-option-title" id="ansible-collections-community-crypto-openssl-csr-module-parameter-custom-extensions-value-raw"><strong>value_raw</strong></p>
+<a class="ansibleOptionLink" href="#parameter-custom_extensions/value_raw" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">string</span></p>
+</div></td>
+<td><div class="ansible-option-indent-desc"></div><div class="ansible-option-cell"><p>The raw value of the custom extension. Will be copied to the extension as-is.</p>
+<p>The value is always assumed to be an UTF8String</p>
+<p>Mutually exclusive with <code class="ansible-option docutils literal notranslate"><strong><a class="reference internal" href="#ansible-collections-community-crypto-openssl-csr-module-parameter-custom-extensions-value"><span class="std std-ref"><span class="pre">custom_extensions[].value</span></span></a></strong></code> and <code class="ansible-option docutils literal notranslate"><strong><a class="reference internal" href="#ansible-collections-community-crypto-openssl-csr-module-parameter-custom-extensions-value-b64"><span class="std std-ref"><span class="pre">custom_extensions[].value_b64</span></span></a></strong></code>.</p>
+</div></td>
+</tr>
+<tr class="row-even"><td><div class="ansible-option-indent"></div><div class="ansible-option-cell">
+<div class="ansibleOptionAnchor" id="parameter-custom_extensions/value_type"></div><p class="ansible-option-title" id="ansible-collections-community-crypto-openssl-csr-module-parameter-custom-extensions-value-type"><strong>value_type</strong></p>
+<a class="ansibleOptionLink" href="#parameter-custom_extensions/value_type" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">string</span></p>
+</div></td>
+<td><div class="ansible-option-indent-desc"></div><div class="ansible-option-cell"><p>The type of the value. Only valid if <code class="ansible-option docutils literal notranslate"><strong><a class="reference internal" href="#ansible-collections-community-crypto-openssl-csr-module-parameter-custom-extensions-value"><span class="std std-ref"><span class="pre">custom_extensions[].value</span></span></a></strong></code> is specified.</p>
+<p>Supported data types are: <code class="ansible-value docutils literal notranslate"><span class="pre">str</span></code>, <code class="ansible-value docutils literal notranslate"><span class="pre">bool</span></code>, <code class="ansible-value docutils literal notranslate"><span class="pre">int</span></code>, <code class="ansible-value docutils literal notranslate"><span class="pre">real</span></code>.</p>
+<p class="ansible-option-line"><strong class="ansible-option-default-bold">Default:</strong> <code class="ansible-option-default docutils literal notranslate"><span class="pre">&quot;str&quot;</span></code></p>
+</div></td>
+</tr>
+<tr class="row-odd"><td><div class="ansible-option-cell">
 <div class="ansibleOptionAnchor" id="parameter-digest"></div><p class="ansible-option-title" id="ansible-collections-community-crypto-openssl-csr-module-parameter-digest"><strong>digest</strong></p>
 <a class="ansibleOptionLink" href="#parameter-digest" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">string</span></p>
 </div></td>
diff --git a/home/runner/work/community.crypto/community.crypto/docsbuild/base/openssl_csr_pipe_module.html b/home/runner/work/community.crypto/community.crypto/docsbuild/head/openssl_csr_pipe_module.html
index 0f30558..6f39611 100644
--- a/home/runner/work/community.crypto/community.crypto/docsbuild/base/openssl_csr_pipe_module.html
+++ b/home/runner/work/community.crypto/community.crypto/docsbuild/head/openssl_csr_pipe_module.html
@@ -215,6 +215,7 @@ see <a class="reference internal" href="#ansible-collections-community-crypto-op
 <p>The below requirements are needed on the host that executes this module.</p>
 <ul class="simple">
 <li><p>cryptography &gt;= 3.3</p></li>
+<li><p>pyasn1 &gt;= 0.4.8 (only if <code class="ansible-option docutils literal notranslate"><strong><a class="reference internal" href="#ansible-collections-community-crypto-openssl-csr-pipe-module-parameter-custom-extensions"><span class="std std-ref"><span class="pre">custom_extensions</span></span></a></strong></code> are specified)</p></li>
 </ul>
 </section>
 <section id="parameters">
@@ -374,6 +375,83 @@ see <a class="reference internal" href="#ansible-collections-community-crypto-op
 </div></td>
 </tr>
 <tr class="row-even"><td><div class="ansible-option-cell">
+<div class="ansibleOptionAnchor" id="parameter-custom_extensions"></div><p class="ansible-option-title" id="ansible-collections-community-crypto-openssl-csr-pipe-module-parameter-custom-extensions"><strong>custom_extensions</strong></p>
+<a class="ansibleOptionLink" href="#parameter-custom_extensions" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">list</span> / <span class="ansible-option-elements">elements=dictionary</span></p>
+<p><em class="ansible-option-versionadded">added in community.crypto 3.1.0</em></p>
+</div></td>
+<td><div class="ansible-option-cell"><p>Allows to specify one or multiple custom extensions.</p>
+<p>The extension value must not be empty, unless <code class="ansible-option docutils literal notranslate"><strong><a class="reference internal" href="#ansible-collections-community-crypto-openssl-csr-pipe-module-parameter-custom-extensions-skip-if-empty"><span class="std std-ref"><span class="pre">custom_extensions[].skip_if_empty</span></span></a></strong></code> is specified.</p>
+<p>In such case the extension will be skipped instead of throwing an error.</p>
+<p>Custom extensions require the <code class="ansible-value docutils literal notranslate"><span class="pre">pyasn1</span></code> Python package to be installed in the environment where the code is run.</p>
+</div></td>
+</tr>
+<tr class="row-odd"><td><div class="ansible-option-indent"></div><div class="ansible-option-cell">
+<div class="ansibleOptionAnchor" id="parameter-custom_extensions/critical"></div><p class="ansible-option-title" id="ansible-collections-community-crypto-openssl-csr-pipe-module-parameter-custom-extensions-critical"><strong>critical</strong></p>
+<a class="ansibleOptionLink" href="#parameter-custom_extensions/critical" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">boolean</span></p>
+</div></td>
+<td><div class="ansible-option-indent-desc"></div><div class="ansible-option-cell"><p>Set the critical flag.</p>
+<p class="ansible-option-line"><strong class="ansible-option-choices">Choices:</strong></p>
+<ul class="simple">
+<li><p><code class="ansible-option-default-bold docutils literal notranslate"><strong><span class="pre">false</span></strong></code> <span class="ansible-option-choices-default-mark">← (default)</span></p></li>
+<li><p><code class="ansible-option-choices-entry docutils literal notranslate"><span class="pre">true</span></code></p></li>
+</ul>
+</div></td>
+</tr>
+<tr class="row-even"><td><div class="ansible-option-indent"></div><div class="ansible-option-cell">
+<div class="ansibleOptionAnchor" id="parameter-custom_extensions/oid"></div><p class="ansible-option-title" id="ansible-collections-community-crypto-openssl-csr-pipe-module-parameter-custom-extensions-oid"><strong>oid</strong></p>
+<a class="ansibleOptionLink" href="#parameter-custom_extensions/oid" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">string</span></p>
+</div></td>
+<td><div class="ansible-option-indent-desc"></div><div class="ansible-option-cell"><p>The OID of the custom extension</p>
+<p>Example: <code class="ansible-value docutils literal notranslate"><span class="pre">1.3.6.1.4.1.34380.1.1.25</span></code>.</p>
+</div></td>
+</tr>
+<tr class="row-odd"><td><div class="ansible-option-indent"></div><div class="ansible-option-cell">
+<div class="ansibleOptionAnchor" id="parameter-custom_extensions/skip_if_empty"></div><p class="ansible-option-title" id="ansible-collections-community-crypto-openssl-csr-pipe-module-parameter-custom-extensions-skip-if-empty"><strong>skip_if_empty</strong></p>
+<a class="ansibleOptionLink" href="#parameter-custom_extensions/skip_if_empty" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">boolean</span></p>
+</div></td>
+<td><div class="ansible-option-indent-desc"></div><div class="ansible-option-cell"><p>Allow empty value to be specified. In such case the extension will be skipped when processing.</p>
+<p class="ansible-option-line"><strong class="ansible-option-choices">Choices:</strong></p>
+<ul class="simple">
+<li><p><code class="ansible-option-default-bold docutils literal notranslate"><strong><span class="pre">false</span></strong></code> <span class="ansible-option-choices-default-mark">← (default)</span></p></li>
+<li><p><code class="ansible-option-choices-entry docutils literal notranslate"><span class="pre">true</span></code></p></li>
+</ul>
+</div></td>
+</tr>
+<tr class="row-even"><td><div class="ansible-option-indent"></div><div class="ansible-option-cell">
+<div class="ansibleOptionAnchor" id="parameter-custom_extensions/value"></div><p class="ansible-option-title" id="ansible-collections-community-crypto-openssl-csr-pipe-module-parameter-custom-extensions-value"><strong>value</strong></p>
+<a class="ansibleOptionLink" href="#parameter-custom_extensions/value" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">string</span></p>
+</div></td>
+<td><div class="ansible-option-indent-desc"></div><div class="ansible-option-cell"><p>The value of the custom extension.</p>
+<p>Mutually exclusive with <code class="ansible-option docutils literal notranslate"><strong><a class="reference internal" href="#ansible-collections-community-crypto-openssl-csr-pipe-module-parameter-custom-extensions-value-raw"><span class="std std-ref"><span class="pre">custom_extensions[].value_raw</span></span></a></strong></code> and <code class="ansible-option docutils literal notranslate"><strong><a class="reference internal" href="#ansible-collections-community-crypto-openssl-csr-pipe-module-parameter-custom-extensions-value-b64"><span class="std std-ref"><span class="pre">custom_extensions[].value_b64</span></span></a></strong></code>.</p>
+</div></td>
+</tr>
+<tr class="row-odd"><td><div class="ansible-option-indent"></div><div class="ansible-option-cell">
+<div class="ansibleOptionAnchor" id="parameter-custom_extensions/value_b64"></div><p class="ansible-option-title" id="ansible-collections-community-crypto-openssl-csr-pipe-module-parameter-custom-extensions-value-b64"><strong>value_b64</strong></p>
+<a class="ansibleOptionLink" href="#parameter-custom_extensions/value_b64" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">string</span></p>
+</div></td>
+<td><div class="ansible-option-indent-desc"></div><div class="ansible-option-cell"><p>The raw value of the custom extension, base64 encoded. Will be decoded, then copied to the extension as-is.</p>
+<p>Mutually exclusive with <code class="ansible-option docutils literal notranslate"><strong><a class="reference internal" href="#ansible-collections-community-crypto-openssl-csr-pipe-module-parameter-custom-extensions-value"><span class="std std-ref"><span class="pre">custom_extensions[].value</span></span></a></strong></code> and <code class="ansible-option docutils literal notranslate"><strong><a class="reference internal" href="#ansible-collections-community-crypto-openssl-csr-pipe-module-parameter-custom-extensions-value-raw"><span class="std std-ref"><span class="pre">custom_extensions[].value_raw</span></span></a></strong></code>.</p>
+</div></td>
+</tr>
+<tr class="row-even"><td><div class="ansible-option-indent"></div><div class="ansible-option-cell">
+<div class="ansibleOptionAnchor" id="parameter-custom_extensions/value_raw"></div><p class="ansible-option-title" id="ansible-collections-community-crypto-openssl-csr-pipe-module-parameter-custom-extensions-value-raw"><strong>value_raw</strong></p>
+<a class="ansibleOptionLink" href="#parameter-custom_extensions/value_raw" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">string</span></p>
+</div></td>
+<td><div class="ansible-option-indent-desc"></div><div class="ansible-option-cell"><p>The raw value of the custom extension. Will be copied to the extension as-is.</p>
+<p>The value is always assumed to be an UTF8String</p>
+<p>Mutually exclusive with <code class="ansible-option docutils literal notranslate"><strong><a class="reference internal" href="#ansible-collections-community-crypto-openssl-csr-pipe-module-parameter-custom-extensions-value"><span class="std std-ref"><span class="pre">custom_extensions[].value</span></span></a></strong></code> and <code class="ansible-option docutils literal notranslate"><strong><a class="reference internal" href="#ansible-collections-community-crypto-openssl-csr-pipe-module-parameter-custom-extensions-value-b64"><span class="std std-ref"><span class="pre">custom_extensions[].value_b64</span></span></a></strong></code>.</p>
+</div></td>
+</tr>
+<tr class="row-odd"><td><div class="ansible-option-indent"></div><div class="ansible-option-cell">
+<div class="ansibleOptionAnchor" id="parameter-custom_extensions/value_type"></div><p class="ansible-option-title" id="ansible-collections-community-crypto-openssl-csr-pipe-module-parameter-custom-extensions-value-type"><strong>value_type</strong></p>
+<a class="ansibleOptionLink" href="#parameter-custom_extensions/value_type" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">string</span></p>
+</div></td>
+<td><div class="ansible-option-indent-desc"></div><div class="ansible-option-cell"><p>The type of the value. Only valid if <code class="ansible-option docutils literal notranslate"><strong><a class="reference internal" href="#ansible-collections-community-crypto-openssl-csr-pipe-module-parameter-custom-extensions-value"><span class="std std-ref"><span class="pre">custom_extensions[].value</span></span></a></strong></code> is specified.</p>
+<p>Supported data types are: <code class="ansible-value docutils literal notranslate"><span class="pre">str</span></code>, <code class="ansible-value docutils literal notranslate"><span class="pre">bool</span></code>, <code class="ansible-value docutils literal notranslate"><span class="pre">int</span></code>, <code class="ansible-value docutils literal notranslate"><span class="pre">real</span></code>.</p>
+<p class="ansible-option-line"><strong class="ansible-option-default-bold">Default:</strong> <code class="ansible-option-default docutils literal notranslate"><span class="pre">&quot;str&quot;</span></code></p>
+</div></td>
+</tr>
+<tr class="row-even"><td><div class="ansible-option-cell">
 <div class="ansibleOptionAnchor" id="parameter-digest"></div><p class="ansible-option-title" id="ansible-collections-community-crypto-openssl-csr-pipe-module-parameter-digest"><strong>digest</strong></p>
 <a class="ansibleOptionLink" href="#parameter-digest" title="Permalink to this option"></a><p class="ansible-option-type-line"><span class="ansible-option-type">string</span></p>
 </div></td>

@zajdee
Copy link
Contributor Author

zajdee commented Nov 4, 2025

@felixfontein Hello, unfortunately I cannot add you as a reviewer, I hope you don't mind this ping. When you have time, could you please check this PR? Thank you.

@felixfontein
Copy link
Contributor

@zajdee thanks for your contribution; I'm currently pretty busy with too many other things, but I should find time soon (maybe already on the weekend) to take a closer look at your PR!

Copy link
Contributor

@felixfontein felixfontein left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks again for your contribution! I've had time for a first review pass.

support: full
requirements:
- cryptography >= 3.3
- pyasn1 >= 0.4.8 (only if O(custom_extensions) are specified)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm against adding this dependency to the module. We already have some code for doing simple ASN.1 encoding (plugins/module_utils/_crypto/_asn1.py), which can be extended. Having to rely on another dependency for simple tasks is not OK.

Also for the first version, I would remove the code that allows to encode ASN.1 completely, and simply allow to provide the extension's value in Base64 encoded form.

description:
- The value of the custom extension.
- Mutually exclusive with O(custom_extensions[].value_raw) and O(custom_extensions[].value_b64).
type: str
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd use

Suggested change
type: str
type: raw

here. That way inputs are not forcible converted to strings before they can be handled.

value_type:
description:
- The type of the value. Only valid if O(custom_extensions[].value) is specified.
- 'Supported data types are: V(str), V(bool), V(int), V(real).'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove V(real). Handling strings, booleans, and integers should be OK.

value_raw:
description:
- The raw value of the custom extension. Will be copied to the extension as-is.
- The value is always assumed to be an UTF8String
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the difference of using value with value_type=str?

type: str
value_b64:
description:
- The raw value of the custom extension, base64 encoded. Will be decoded, then copied to the extension as-is.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- The raw value of the custom extension, base64 encoded. Will be decoded, then copied to the extension as-is.
- The raw value of the custom extension, Base64 encoded. Will be decoded, then copied to the extension as-is.

Comment on lines +161 to +171
if not value and not value_raw and not value_b64:
if custom_extension.get("skip_if_empty"):
continue
raise OpenSSLObjectError(
f"neither of value, value_raw, or value_b64 was specified (oid {oid})"
)

if sum(bool(x) for x in [value, value_raw, value_b64]) != 1:
raise OpenSSLObjectError(
f"exactly one of value, value_raw, or value_b64 can be set (oid {oid})"
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already checked by Ansible.

Suggested change
if not value and not value_raw and not value_b64:
if custom_extension.get("skip_if_empty"):
continue
raise OpenSSLObjectError(
f"neither of value, value_raw, or value_b64 was specified (oid {oid})"
)
if sum(bool(x) for x in [value, value_raw, value_b64]) != 1:
raise OpenSSLObjectError(
f"exactly one of value, value_raw, or value_b64 can be set (oid {oid})"
)

f"exactly one of value, value_raw, or value_b64 can be set (oid {oid})"
)

if value_raw or value_b64:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Always compare with None to check whether something was specified:

Suggested change
if value_raw or value_b64:
if value_raw is not None or value_b64 is not None:

) = None
self.custom_extensions: (
list[tuple[cryptography.x509.UnrecognizedExtension, bool]] | None
) = None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove the | None and set it to []. There's no need to distinguish between None and [].

if custom_extensions:
self.custom_extensions = parse_custom_extensions(
module=module, custom_extensions=custom_extensions
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm missing the code that checks for collisions between custom_extensions and extensions specified through other module options (see #401).

"elements": "dict",
"options": {
"critical": {"type": "bool", "default": False},
"oid": {"type": "str"},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the oid be required?

Suggested change
"oid": {"type": "str"},
"oid": {"type": "str", "required": True},

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

openssl_csr: allow to add arbitrary extensions

2 participants