Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix CAA records whitespace splitting with additional optional properties #92

Merged
merged 4 commits into from
May 8, 2024

Conversation

jmittermair
Copy link
Contributor

@jmittermair jmittermair commented May 4, 2024

  • Currently, if there are additional properties inside of the CAA record that are separated by a whitespace, the provider will fail with the following error message:
2024-05-04T15:40:27  [140469743658816] INFO  Manager dump: using custom YamlProvider
Traceback (most recent call last):
  File "/home/james/dev/python/octodns/env/bin/octodns-dump", line 8, in <module>
    sys.exit(main())
  File "/home/james/dev/python/octodns/env/lib/python3.10/site-packages/octodns/cmds/dump.py", line 51, in main
    manager.dump(
  File "/home/james/dev/python/octodns/env/lib/python3.10/site-packages/octodns/manager.py", line 969, in dump
    source.populate(zone, lenient=lenient)
  File "/home/james/dev/python/octodns/env/lib/python3.10/site-packages/octodns_route53/provider.py", line 1272, in populate
    data = getattr(self, f'_data_for_{record_type}')(rrset)
  File "/home/james/dev/python/octodns/env/lib/python3.10/site-packages/octodns_route53/provider.py", line 916, in _data_for_CAA
    flags, tag, value = rr['Value'].split()
ValueError: too many values to unpack (expected 3)
5.2.  CAA issue Property

   The issue property tag is used to request that certificate issuers
   perform CAA issue restriction processing for the domain and to grant
   authorization to specific certificate issuers.

   The CAA issue property value has the following sub-syntax (specified
   in ABNF as per [RFC5234]).

   issuevalue  = space [domain] space [";" *(space parameter) space]

   domain = label *("." label)
   label = (ALPHA / DIGIT) *( *("-") (ALPHA / DIGIT))

   space = *(SP / HTAB)

   parameter =  tag "=" value

   tag = 1*(ALPHA / DIGIT)

   value = *VCHAR

   For consistency with other aspects of DNS administration, domain name
   values are specified in letter-digit-hyphen Label (LDH-Label) form.
  • This fix uses the split(..., maxsplit) parameter, to ensure that we only split the first 2 whitespace characters between the FIELD, TAG and VALUE, whilst leaving any additional whitespace untouched.

Note:
(This is my first public pull request -- lint was successful and test coverage passes. Please let me know if I have missed anything).

octodns-route53 on  fix-caa-records-whitespace-split [!?] via 🐍 v3.10.12 (env) via ❄️  impure (shell) 
❯ ./script/lint
echo $
octodns-route53 on  fix-caa-records-whitespace-split [!?] via 🐍 v3.10.12 (env) via ❄️  impure (shell) 
❯ echo $?
0

octodns-route53 on  fix-caa-records-whitespace-split [!?] via 🐍 v3.10.12 (env) via ❄️  impure (shell) 
❯ ./script/coverage
==================================================================================================================== test session starts =====================================================================================================================
platform linux -- Python 3.10.12, pytest-8.1.1, pluggy-1.4.0
rootdir: /home/jmitterm/dev/python/octodns/providers_source/octodns-route53
configfile: pyproject.toml
plugins: typeguard-2.13.3, network-0.0.1, cov-5.0.0, mock-3.14.0
collected 63 items                                                                                                                                                                                                                                           

tests/test_octodns_provider_route53.py .....................................................                                                                                                                                                           [ 84%]
tests/test_octodns_source_ec2.py ......                                                                                                                                                                                                                [ 93%]
tests/test_octodns_source_elb.py ....                                                                                                                                                                                                                  [100%]

---------- coverage: platform linux, python 3.10.12-final-0 ----------
Name                                    Stmts   Miss Branch BrPart  Cover
-------------------------------------------------------------------------
octodns_route53/__init__.py                 9      0      0      0   100%
octodns_route53/auth.py                    24      0      8      0   100%
octodns_route53/processor/__init__.py      14      0      4      0   100%
octodns_route53/provider.py               846      0    322      0   100%
octodns_route53/record.py                  69      0     18      0   100%
octodns_route53/source.py                 138      0     74      0   100%
-------------------------------------------------------------------------
TOTAL                                    1100      0    426      0   100%
Coverage HTML written to dir htmlcov
Coverage XML written to file coverage.xml

Required test coverage of 100% reached. Total coverage: 100.00%

===================================================================================================================== 63 passed in 1.15s =====================================================================================================================

octodns-route53 on  fix-caa-records-whitespace-split [!?] via 🐍 v3.10.12 (env) via ❄️  impure (shell)

- Currently, CAA records with whitespace inside of the value will fail
  to tuple unpack, because split expects only two spaces.
- Added fix to force split to only split twice, instead of as many spaces
  as are detected, since a valid CAA record shouldn't have more than two
  whitespace characters separating between the flag, tag and value.
- As per RFC 6844, optional parameters are defined as the below:

```
5.2.  CAA issue Property

   The issue property tag is used to request that certificate issuers
   perform CAA issue restriction processing for the domain and to grant
   authorization to specific certificate issuers.

   The CAA issue property value has the following sub-syntax (specified
   in ABNF as per [RFC5234]).

   issuevalue  = space [domain] space [";" *(space parameter) space]

   domain = label *("." label)
   label = (ALPHA / DIGIT) *( *("-") (ALPHA / DIGIT))

   space = *(SP / HTAB)

   parameter =  tag "=" value

   tag = 1*(ALPHA / DIGIT)

   value = *VCHAR

   For consistency with other aspects of DNS administration, domain name
   values are specified in letter-digit-hyphen Label (LDH-Label) form.
```

- This allows for records that look like the below:
```
CAA 0 issue "cert.example.com; cansignhttpexchanges=yes"
```
- Added property 'cansignhttpexchanges=yes' to CAA record test.
- Whitespace is added as this is the example provided in RFC 6844.
- This change addresses failure to tuple unpack, caused by whitespaces
  in optional issuer parameters, when following RFC conventions.
@jmittermair jmittermair marked this pull request as ready for review May 4, 2024 06:46
Copy link
Contributor

@ross ross left a comment

Choose a reason for hiding this comment

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

Interesting. I guess this is part of the CAA spec? I see some similar bits after a quick look at https://datatracker.ietf.org/doc/html/rfc6844#section-3, but hadn't run across them in the wild before.

We may need to look into supporting these in the core record type and it's rdata processing. https://github.com/octodns/octodns/blob/ed13d2db52b4127f1035a8927a396467219b9918/octodns/record/caa.py#L16 as it would appear to have the same issue.

Just treating anything else in the last section as part of the value seems like a workable way to go about this, but I'd need to think about it for a bit as to whether it'd be better to separate the tags out into a dict, e.g.

foo:
  type: CAA
  values:
    - flags: 0
      tag: issue
      value: ca.unit.tests
      params:
        cansignhttpexchanges: yes

Any thoughts on that?

Even though route53's current implementatiomn is not using octodns core's rdata parsing it'd be best to keep them in sync so would like to hold off on this until there's a broader decision on the path.

/cc @octodns/review for thoughts?

@jmittermair
Copy link
Contributor Author

Thanks for that Ross,

I did think it was possible that this was happening on other providers, but didn't have the time to check. I noticed this when trying to run octodns-dump and hit the error; it is possible to workaround the error by removing spaces inside of the quotes "ca.net;property=value", but it seems unintuitive to have to do that just to be able to use OctoDNS, when all examples online for this seem to suggest spaces are correct here, (although both work and are valid CAA records).

I would agree that having a YAML params section is cleaner looking as far as YAML goes, but at the same time, the optional params ARE a part of the value in the spec, so that's one way to consider it, that we could keep it with this behaviour. Also, I guess I did not name my PR very well, considering.

To be clear, the main widely supported optional property that I am aware of is the one I used in the test, (it's required for AMP), but a CA can add any properties they want in their implementation of issuing certificates, and if there are CAA records present on a domain that don't implement the additional property, then they can't issue according to the spec. So additional properties might be more heavily used with something like an internal self-hosted CA authority at a company.

Another potential fix we could employ is something like the following line [1], which works for all records that Route53 accepts. (The validation inside of Route53 specifically does not allow for any additional spaces outside of the quotation marks, and never allows more than the flag, tag and value).

flags, tag, *value = rr['Value'].split()
values.append({'flags': flags, 'tag': tag, 'value': " ".join(value)})

It's a bit cleaner as we just let the tuple unpacking inherit all remaining values from the split, (and if you did want to add a params section, then that could be done by getting rid of the join and only keeping value[0] here.

I hope that all makes sense, and if not, please let me know so I can fix up anything ambiguous.

Thanks!

Copy link
Contributor

@ross ross left a comment

Choose a reason for hiding this comment

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

Good point. Guess it's similar to SPF values in TXT records where there's data encoded into the value itself. Makes sense to leave these alone and just use your splitting logic. will do the same over in octoDNS core (and potentially other modules that haven't yet moved over to using the core's rdata handling.)

@ross
Copy link
Contributor

ross commented May 5, 2024

Looks like formatting is off. ./script/format should fix that. You can run everything locally that CI does with ./script/bootstrap (once) and then ./.git_hooks_pre-commit. The bootstrap script will also install that locally as a pre-commit hook.

@jmittermair
Copy link
Contributor Author

Hi Ross,

Thanks for your help, I've fixed it, please try the CI again.

(I didn't notice the git precommit hook failing, as I run NixOS, I have to modify any scripts that use shebangs to instead use a Nix shebang.)
https://nixos.wiki/wiki/Nix-shell_shebang

Next time, I'll set up a Fedora dev container to make this a bit easier.

Cheers,
James.

@istr
Copy link

istr commented May 7, 2024

Just treating anything else in the last section as part of the value seems like a workable way to go about this, but I'd need to think about it for a bit as to whether it'd be better to separate the tags out into a dict, e.g.

foo:
  type: CAA
  values:
    - flags: 0
      tag: issue
      value: ca.unit.tests
      params:
        cansignhttpexchanges: yes

Any thoughts on that?

I would follow jmittermair's argument

at the same time, the optional params ARE a part of the value in the spec, so that's one way to consider it, that we could keep it with this behaviour.

This would make it easy to write (and verify) an ABNF-based validation that is close to the spec.

Even though route53's current implementation is not using octodns core's rdata parsing it'd be best to keep them in sync so would like to hold off on this until there's a broader decision on the path.

It would be great if we could adopt the proposed patch to the octoDNS core and then invite the route53 (or other provider's) contributors to switch to using the core's rdata capabilities.

@ross
Copy link
Contributor

ross commented May 8, 2024

octodns/octodns#1171 has shipped to address this in octoDNS core.

@ross ross merged commit 278a998 into octodns:main May 8, 2024
7 checks passed
@ross
Copy link
Contributor

ross commented May 8, 2024

It would be great if we could adopt the proposed patch to the octoDNS core and then invite the route53 (or other provider's) contributors to switch to using the core's rdata capabilities.

That's been on my TODO list for quite a while, but it hasn't bubbled up to the top.

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.

3 participants