| @ -0,0 +1,35 @@ | |||
| ### This is all a WIP | |||
| This is a WIP and will definitely be rough around the edges, but in the spirit | |||
| of not letting perfect get in the way of good enough, or really existing at | |||
| all. It's being uploaded as a starting point. PRs/suggestions welcome as are | |||
| ideas for other subjects to cover. | |||
| ### Examples | |||
| * Getting started with a [basic octoDNS configuration](basic/) - new to octoDNS | |||
| this is the place to start. It'll walk you through the main pieces of DNS IaC | |||
| with octoDNS including the process of planning and applying changes. | |||
| * [Migrating to octoDNS](migrating-to-octodns/) - have an existing DNS setup | |||
| you'd like to bring into octoDNS check this example out right after | |||
| [basic](basic/). It'll walk you through the steps of using `octodns-dump` to | |||
| pull the existing data out of your provider into matching YAML config files on | |||
| disk. | |||
| ### Running PowerDNS | |||
| If you'd like to play around with running the examples in this directory | |||
| interactively you'll need a target for pushing data to. | |||
| [octodns-powerdns](https://github.com/octodns/octodns-powerdns) is the best | |||
| stand-alone option for this and the examples directory makes extensive use of | |||
| it. There is a [docker-compose.yml](docker-compose.yml) file that should get a | |||
| fully functional copy of PowerDNS backed my MySQL with the API enabled along | |||
| with other relivant functionality. For any of the examples that refer to the | |||
| local PowerDNS instance the following instructions below should get it up and | |||
| running. | |||
| 1. If you haven't already [install docker compose](https://docs.docker.com/compose/install/) | |||
| 1. If you don't already have a copy of octoDNS checked out run `git clone https://github.com/octodns/octodns.git` | |||
| 1. In a seperate terminal window or tab | |||
| 1. cd into the examples directory `cd octodns/examples` | |||
| 1. Run docker-compose up `docker-compose up`, this will start up MySQL and PowerDNS running them in the foreground with their logs printing to the terminal | |||
| @ -0,0 +1,254 @@ | |||
| ## Basic octoDNS Setup | |||
| This is the starting point octoDNS config, it's pretty similar to what you | |||
| might see for managing a set of personal domains or a small business. | |||
| Most of the actual documentation for this example is found in the comments in | |||
| the YAML configuration files. | |||
| * [config/octodns.yaml](config/octodns.yaml) | |||
| * [config/my-domain.com.yaml](config/my-domain.com.yaml) | |||
| * [config/unused-domain.io.yaml](config/unused-domain.io.yaml) | |||
| From here on this README focuses on the general process of running octoDNS. | |||
| ## Checking out the code and setting up the environment | |||
| You would not normally need to check out octoDNS itself, you instead would have | |||
| a git repo with only your configuration files. Here we're cloning the repo only | |||
| to get a copy of the example files. | |||
| ```console | |||
| $ git clone https://github.com/octodns/octodns.git | |||
| $ cd octodns/examples/basic/ | |||
| $ python3 -mvenv env | |||
| $ source ../env.sh | |||
| $ source env/bin/activate | |||
| (env) $ pip install -r requirements.txt | |||
| ``` | |||
| Finally check out [Running PowerDNS](../README.md#running-powerdns) to get a local | |||
| instance of PowerDNS up and going before continuing. | |||
| ## Running octoDNS the first time | |||
| Once you have your configuration files and octoDNS installed you're ready to | |||
| run the sync command to get it to plan an initial set of changes. | |||
| ```console | |||
| (env) $ octodns-sync --config-file=config/octodns.yaml | |||
| 2023-08-23T15:09:51 [4577488384] INFO Manager __init__: config_file=config/octodns.yaml, (octoDNS 1.0.1) | |||
| 2023-08-23T15:09:51 [4577488384] INFO Manager _config_executor: max_workers=1 | |||
| 2023-08-23T15:09:51 [4577488384] INFO Manager _config_include_meta: include_meta=False | |||
| 2023-08-23T15:09:51 [4577488384] INFO Manager _config_auto_arpa: auto_arpa=False | |||
| 2023-08-23T15:09:51 [4577488384] INFO Manager __init__: global_processors=[] | |||
| 2023-08-23T15:09:51 [4577488384] INFO Manager __init__: provider=config (octodns.provider.yaml 1.0.1) | |||
| 2023-08-23T15:09:51 [4577488384] INFO SpfSource[no-mail] __init__: id=no-mail, a_records=[], mx_records=[], ip4_addresses=[], ip6_addresses=[], includes=[], exists=[], soft_fail=False, merging_enabled=False, ttl=3600 | |||
| 2023-08-23T15:09:51 [4577488384] INFO Manager __init__: provider=no-mail (octodns_spf 0.0.2) | |||
| 2023-08-23T15:09:51 [4577488384] INFO Manager __init__: provider=yaml (octodns.provider.yaml 1.0.1) | |||
| 2023-08-23T15:09:51 [4577488384] INFO Manager sync: eligible_zones=[], eligible_targets=[], dry_run=True, force=False, plan_output_fh=<stdout> | |||
| 2023-08-23T15:09:51 [4577488384] INFO Manager sync: sources=['config', 'no-mail'] | |||
| 2023-08-23T15:09:51 [4577488384] INFO Manager sync: dynamic zone=*, sources=[YamlProvider, SpfSource] | |||
| 2023-08-23T15:09:51 [4577488384] INFO Manager sync: adding dynamic zone=my-domain.com. | |||
| 2023-08-23T15:09:51 [4577488384] INFO Manager sync: adding dynamic zone=unused-domain.io. | |||
| 2023-08-23T15:09:51 [4577488384] INFO Manager sync: zone=my-domain.com. | |||
| 2023-08-23T15:09:51 [4577488384] INFO Manager sync: sources=['config', 'no-mail'] | |||
| 2023-08-23T15:09:51 [4577488384] INFO Manager sync: targets=['yaml'] | |||
| 2023-08-23T15:09:51 [4577488384] INFO Manager sync: zone=unused-domain.io. | |||
| 2023-08-23T15:09:51 [4577488384] INFO Manager sync: sources=['config', 'no-mail'] | |||
| 2023-08-23T15:09:51 [4577488384] INFO Manager sync: targets=['yaml'] | |||
| 2023-08-23T15:09:51 [4577488384] INFO YamlProvider[config] populate: found 9 records, exists=False | |||
| 2023-08-23T15:09:51 [4577488384] INFO SpfSource[no-mail] populate: found 0 records, exists=False | |||
| 2023-08-23T15:09:51 [4577488384] INFO PowerDnsProvider[powerdns] plan: desired=my-domain.com. | |||
| 2023-08-23T15:09:51 [4577488384] WARNING PowerDnsProvider[powerdns] root NS record supported, but no record is configured for my-domain.com. | |||
| 2023-08-23T15:09:51 [4577488384] INFO PowerDnsProvider[powerdns] plan: Creates=9, Updates=0, Deletes=0, Existing Records=0 | |||
| 2023-08-23T15:09:51 [4577488384] INFO YamlProvider[config] populate: found 0 records, exists=False | |||
| 2023-08-23T15:09:51 [4577488384] INFO SpfSource[no-mail] populate: found 1 records, exists=False | |||
| 2023-08-23T15:09:51 [4577488384] INFO PowerDnsProvider[powerdns] plan: desired=unused-domain.io. | |||
| 2023-08-23T15:09:51 [4577488384] WARNING PowerDnsProvider[powerdns] root NS record supported, but no record is configured for unused-domain.io. | |||
| 2023-08-23T15:09:51 [4577488384] INFO PowerDnsProvider[powerdns] plan: Creates=1, Updates=0, Deletes=0, Existing Records=0 | |||
| 2023-08-23T15:09:51 [4577488384] INFO Plan | |||
| ******************************************************************************** | |||
| * unused-domain.io. | |||
| ******************************************************************************** | |||
| * powerdns (PowerDnsProvider) | |||
| * Create Zone<unused-domain.io.> | |||
| * Create <TxtRecord TXT 3600, unused-domain.io., ['v=spf1 -all']> () | |||
| * Summary: Creates=1, Updates=0, Deletes=0, Existing Records=0 | |||
| ******************************************************************************** | |||
| * my-domain.com. | |||
| ******************************************************************************** | |||
| * powerdns (PowerDnsProvider) | |||
| * Create Zone<my-domain.com.> | |||
| * Create <ARecord A 3600, my-domain.com., ['203.0.113.42', '203.0.113.43']> (config) | |||
| * Create <AaaaRecord AAAA 3600, my-domain.com., ['2001:db8::44']> (config) | |||
| * Create <TxtRecord TXT 3600, my-domain.com., ['some-verification=3becb991-932f-4433-a280-9df6f39b6194', 'v=spf1 -all', 'z-other-thing=this proves i have control over this domain']> (config) | |||
| * Create <ARecord A 3600, *.my-domain.com., ['203.0.113.45']> (config) | |||
| * Create <AaaaRecord AAAA 3600, *.my-domain.com., ['2001:db8::46']> (config) | |||
| * Create <TxtRecord TXT 3600, nadcbiqkbgq._companyname.my-domain.com., ['a-different-proof-of-ownership']> (config) | |||
| * Create <CnameRecord CNAME 3600, pointer.my-domain.com., look.over-here.net.> (config) | |||
| * Create <ARecord A 3600, www.my-domain.com., ['203.0.113.42', '203.0.113.43']> (config) | |||
| * Create <AaaaRecord AAAA 3600, www.my-domain.com., ['2001:db8::44']> (config) | |||
| * Summary: Creates=9, Updates=0, Deletes=0, Existing Records=0 | |||
| ******************************************************************************** | |||
| ``` | |||
| ### The log output | |||
| It's always a good idea to scan over the logging output of an octoDNS run. Most | |||
| of it is informational, telling you what has been configured and providing a | |||
| big picture idea of what's happening while planning. You will sometimes see | |||
| WARNINGS, these are generally telling you that there's something you should | |||
| think about or look into. | |||
| In the run above there was a warning about root/APEX NS records being supported | |||
| by the provider, YamlProvider, and not present in the config. Not all providers | |||
| support managing the root NS records, some just hard code their own name | |||
| servers. | |||
| ```console | |||
| 2023-08-23T15:09:51 [4577488384] WARNING PowerDnsProvider[powerdns] root NS record supported, but no record is configured for my-domain.com. | |||
| ``` | |||
| ### The plan output | |||
| ```console | |||
| ******************************************************************************** | |||
| * unused-domain.io. | |||
| ******************************************************************************** | |||
| * powerdns (PowerDnsProvider) | |||
| * Create Zone<unused-domain.io.> | |||
| * Create <TxtRecord TXT 3600, unused-domain.io., ['v=spf1 -all']> () | |||
| * Summary: Creates=1, Updates=0, Deletes=0, Existing Records=0 | |||
| ******************************************************************************** | |||
| * my-domain.com. | |||
| ******************************************************************************** | |||
| * powerdns (PowerDnsProvider) | |||
| * Create Zone<my-domain.com.> | |||
| * Create <ARecord A 3600, my-domain.com., ['203.0.113.42', '203.0.113.43']> (config) | |||
| * Create <AaaaRecord AAAA 3600, my-domain.com., ['2001:db8::44']> (config) | |||
| * Create <TxtRecord TXT 3600, my-domain.com., ['some-verification=3becb991-932f-4433-a280-9df6f39b6194', 'v=spf1 -all', 'z-other-thing=this proves i have control over this domain']> (config) | |||
| * Create <ARecord A 3600, *.my-domain.com., ['203.0.113.45']> (config) | |||
| * Create <AaaaRecord AAAA 3600, *.my-domain.com., ['2001:db8::46']> (config) | |||
| * Create <TxtRecord TXT 3600, nadcbiqkbgq._companyname.my-domain.com., ['a-different-proof-of-ownership']> (config) | |||
| * Create <CnameRecord CNAME 3600, pointer.my-domain.com., look.over-here.net.> (config) | |||
| * Create <ARecord A 3600, www.my-domain.com., ['203.0.113.42', '203.0.113.43']> (config) | |||
| * Create <AaaaRecord AAAA 3600, www.my-domain.com., ['2001:db8::44']> (config) | |||
| * Summary: Creates=9, Updates=0, Deletes=0, Existing Records=0 | |||
| ******************************************************************************** | |||
| ``` | |||
| ### Applying the changes | |||
| ```console | |||
| (env) $ octodns-sync --config-file=config/octodns.yaml --doit | |||
| ... | |||
| ******************************************************************************** | |||
| * unused-domain.io. | |||
| ******************************************************************************** | |||
| * powerdns (PowerDnsProvider) | |||
| * Create Zone<unused-domain.io.> | |||
| * Create <TxtRecord TXT 3600, unused-domain.io., ['v=spf1 -all']> () | |||
| * Summary: Creates=1, Updates=0, Deletes=0, Existing Records=0 | |||
| ******************************************************************************** | |||
| * my-domain.com. | |||
| ******************************************************************************** | |||
| * powerdns (PowerDnsProvider) | |||
| * Create Zone<my-domain.com.> | |||
| * Create <ARecord A 3600, my-domain.com., ['203.0.113.42', '203.0.113.43']> (config) | |||
| * Create <AaaaRecord AAAA 3600, my-domain.com., ['2001:db8::44']> (config) | |||
| * Create <TxtRecord TXT 3600, my-domain.com., ['some-verification=3becb991-932f-4433-a280-9df6f39b6194', 'v=spf1 -all', 'z-other-thing=this proves i have control over this domain']> (config) | |||
| * Create <ARecord A 3600, *.my-domain.com., ['203.0.113.45']> (config) | |||
| * Create <AaaaRecord AAAA 3600, *.my-domain.com., ['2001:db8::46']> (config) | |||
| * Create <TxtRecord TXT 3600, nadcbiqkbgq._companyname.my-domain.com., ['a-different-proof-of-ownership']> (config) | |||
| * Create <CnameRecord CNAME 3600, pointer.my-domain.com., look.over-here.net.> (config) | |||
| * Create <ARecord A 3600, www.my-domain.com., ['203.0.113.42', '203.0.113.43']> (config) | |||
| * Create <AaaaRecord AAAA 3600, www.my-domain.com., ['2001:db8::44']> (config) | |||
| * Summary: Creates=9, Updates=0, Deletes=0, Existing Records=0 | |||
| ******************************************************************************** | |||
| 2023-08-23T15:17:00 [4671815168] INFO PowerDnsProvider[powerdns] apply: making 1 changes to unused-domain.io. | |||
| 2023-08-23T15:17:00 [4671815168] INFO PowerDnsProvider[powerdns] apply: making 9 changes to my-domain.com. | |||
| 2023-08-23T15:17:00 [4671815168] INFO Manager sync: 10 total changes | |||
| ``` | |||
| ```console | |||
| (env) $ octodns-sync --config-file=config/octodns.yaml | |||
| ******************************************************************************** | |||
| No changes were planned | |||
| ******************************************************************************** | |||
| ``` | |||
| That's it. You're now managing your DNS with octoDNS. | |||
| ## Making a change | |||
| At some point down the road you need to make a change, maybe one of your web | |||
| boxes was replaced and there's a new IP address. The first step is to open up | |||
| the zone's YAML file and modify the YAML, removing 203.0.113.42 and adding | |||
| 203.0.113.44. | |||
| ## Running octoDNS to see the plan | |||
| Just like during the first run section above we'll first run octoDNS to see what changes it would make. | |||
| We'd skim the log lines again looking for unexpected WARNINGS and then take a look at the changes. | |||
| ```console | |||
| (env) $ octodns-sync --config-file=config/octodns.yaml | |||
| ... | |||
| ******************************************************************************** | |||
| * my-domain.com. | |||
| ******************************************************************************** | |||
| * powerdns (PowerDnsProvider) | |||
| * Create Zone<my-domain.com.> | |||
| * Update | |||
| * <ARecord A 3600, my-domain.com., ['203.0.113.42', '203.0.113.43']> -> | |||
| * <ARecord A 3600, my-domain.com., ['203.0.113.43', '203.0.113.44']> (config) | |||
| * Update | |||
| * <ARecord A 3600, www.my-domain.com., ['203.0.113.42', '203.0.113.43']> -> | |||
| * <ARecord A 3600, www.my-domain.com., ['203.0.113.43', '203.0.113.44']> (config) | |||
| * Summary: Creates=0, Updates=2, Deletes=0, Existing Records=9 | |||
| ******************************************************************************** | |||
| ``` | |||
| Since we have used YAML anchors to share the values across both the root and | |||
| www A's we see that octoDNS will be making changes to both those records. | |||
| If there were unexpected things here we'd need to investigate what's changed. | |||
| Maybe there were other alterations in the YAML that hadn't been applied yet or | |||
| someone modified the records through another means. | |||
| Here we only see the expected changes so we're good to move forward with | |||
| applying them. | |||
| ```console | |||
| (env) $ octodns-sync --config-file=config/octodns.yaml --doit | |||
| ... | |||
| ******************************************************************************** | |||
| * my-domain.com. | |||
| ******************************************************************************** | |||
| * powerdns (PowerDnsProvider) | |||
| * Create Zone<my-domain.com.> | |||
| * Update | |||
| * <ARecord A 3600, my-domain.com., ['203.0.113.42', '203.0.113.43']> -> | |||
| * <ARecord A 3600, my-domain.com., ['203.0.113.43', '203.0.113.44']> (config) | |||
| * Update | |||
| * <ARecord A 3600, www.my-domain.com., ['203.0.113.42', '203.0.113.43']> -> | |||
| * <ARecord A 3600, www.my-domain.com., ['203.0.113.43', '203.0.113.44']> (config) | |||
| * Summary: Creates=0, Updates=2, Deletes=0, Existing Records=9 | |||
| ******************************************************************************** | |||
| ``` | |||
| If we want we can run another plan to make sure there are no further pending changes. | |||
| ```console | |||
| (env) $ octodns-sync --config-file=config/octodns.yaml | |||
| ******************************************************************************** | |||
| No changes were planned | |||
| ******************************************************************************** | |||
| ``` | |||
| ## What's Next | |||
| * Check out [migrating to octoDNS](../migrating-to-octodns) for an example of how to create zone configuration YAML files from your existing provider's configuration | |||
| * For a complete list check out the [Examples Directory](../) | |||
| @ -0,0 +1,58 @@ | |||
| --- | |||
| # These are root/APEX records, things that live at the top-level of the zone. | |||
| # There are multiple records so there's a YAML list, each element in that list | |||
| # defines its own record. | |||
| '': | |||
| # We first have an A record, mapping A my-domain.com to two IPv4 addresses, we | |||
| # also use a YAML anchor to effectively store the values for later use. | |||
| - type: A | |||
| values: &WEB_A_VALUES | |||
| - 203.0.113.42 | |||
| - 203.0.113.43 | |||
| # Similar to the A above, this is a AAAA for my-domain.com with a single IPv6 | |||
| # addresse. | |||
| - type: AAAA | |||
| values: &WEB_AAAA_VALUES | |||
| - 2001:DB8::44 | |||
| # Finally it's common to have to prove ownership of a domain with a TXT value | |||
| # in an APEX record. The following are a couple made up examples of how that | |||
| # would be done. As you'll see when you plan things The SpfSource will merge | |||
| # its SPF value in with the values defined here | |||
| - type: TXT | |||
| values: | |||
| - some-verification=3becb991-932f-4433-a280-9df6f39b6194 | |||
| - z-other-thing=this proves i have control over this domain | |||
| # This is a wildcard record, any A or AAAA query not matching a more specific | |||
| # answer defined in this file will match this name and receive the associated | |||
| # value in response. | |||
| '*': | |||
| - type: A | |||
| # Note here we have a single value rather than values and use `value`. | |||
| value: 203.0.113.45 | |||
| - type: AAAA | |||
| value: 2001:DB8::46 | |||
| # Note that the records in this zone are sorted, by default YamlProvider | |||
| # enforces alphabetical sorting of records and even the keys within record | |||
| # data. This is an organizational best practice, but can be disabled with the | |||
| # enforce_order parameter to YamlProvider | |||
| NADCBiQKBgQ._companyname: | |||
| type: TXT | |||
| value: a-different-proof-of-ownership | |||
| # We want www and the APEX to return the same values for A/AAAA queries. We | |||
| # stored the values earlier in YAML anchors so we can use them now on. | |||
| # This is a CNAME record | |||
| pointer: | |||
| type: CNAME | |||
| # CNAMEs can only have a single value so they require `value` to be used. As a | |||
| # best practice the target name must end with a `.` If you have a specific | |||
| # case where you need to omit the `.`, or other enforced best practices, see | |||
| # the lenience example for more information. | |||
| value: look.over-here.net. | |||
| # This is an example of a record with a `.` in it's name, it would result for a | |||
| # TXT query of NADCBiQKBgQ._companyname.my-domain.com. This specific example is | |||
| # another commonly required proof of overship record. | |||
| www: | |||
| - type: A | |||
| values: *WEB_A_VALUES | |||
| - type: AAAA | |||
| values: *WEB_AAAA_VALUES | |||
| @ -0,0 +1,58 @@ | |||
| --- | |||
| providers: | |||
| # The primary source of record data in most setup is YAML files on disk. By | |||
| # convention it's named config. | |||
| config: | |||
| class: octodns.provider.yaml.YamlProvider | |||
| # There are a number of configuration options available to customize the | |||
| # behavior of the provider, but the defaults are generally suitable and the | |||
| # only required value is the name of the directory where the files can be | |||
| # found. Convention is the same config directory in which this file lives. | |||
| directory: ./config | |||
| # Having a no mail SPF record is considered best practice for any domains | |||
| # that you do not plan to use for email. The octodns-spf module provides | |||
| # SpfSource which by default adds the SPF value `v=spf1 -all` to accomplish | |||
| # this. If you have one or more domains that involve mail check out the email | |||
| # example after you're done here. | |||
| no-mail: | |||
| class: octodns_spf.SpfSource | |||
| # PowerDNS serves as a good option for a locally runnable standalone provider | |||
| # to use in our examples. See the top-level example README for information on | |||
| # how to run it and the documentation for octodns-powerdns for documentation | |||
| # about the details of the config below. | |||
| powerdns: | |||
| class: octodns_powerdns.PowerDnsProvider | |||
| host: 127.0.0.1 | |||
| port: 8081 | |||
| api_key: env/POWERDNS_API_KEY | |||
| timeout: 10 | |||
| # If you wish to push your record data to multiple providers you'd define the | |||
| # other here and include them in the targets list down below. See the | |||
| # multiple-provider example for more details. | |||
| zones: | |||
| # This configuration is using dynamic zone config. Rather than having to | |||
| # explicitly list out all of the zones here with their coresponding | |||
| # configuration there's a single wildcard entry. Here the `config` source | |||
| # will look for zone files defined in its configured directory, the files | |||
| # alongside this one. For more details see the dynamic-zone-config example. | |||
| '*': | |||
| # This is the place(s) we look for the records in each zone | |||
| sources: | |||
| # The conig provider will be firs in line to provide records for the | |||
| # zones, and since we're using dynamic zone config it will also be | |||
| # responsible for defining what zones exist. | |||
| - config | |||
| # After config has added its records the we'll add an no email SPF value. | |||
| # It'll create the APEX TXT record if necessary. Again see the email | |||
| # example for more details. | |||
| - no-mail | |||
| # This is the place(s) we push the record data to when applying changes | |||
| targets: | |||
| # Here we're pushing things to our local PowerDNS instance | |||
| - powerdns | |||
| @ -0,0 +1,2 @@ | |||
| --- | |||
| # Nothing in here, just an empty domain b/c it's not actually used for anything | |||
| @ -0,0 +1,3 @@ | |||
| octodns>=1.0.1 | |||
| octodns_powerdns>=0.0.5 | |||
| octodns_spf>=0.0.2 | |||
| @ -0,0 +1,31 @@ | |||
| --- | |||
| ? '' | |||
| : - type: A | |||
| values: | |||
| - 203.0.113.42 | |||
| - 203.0.113.43 | |||
| - type: AAAA | |||
| value: 2001:db8::44 | |||
| - type: TXT | |||
| values: | |||
| - some-verification=3becb991-932f-4433-a280-9df6f39b6194 | |||
| - v=spf1 -all | |||
| - z-other-thing=this proves i have control over this domain | |||
| '*': | |||
| - type: A | |||
| value: 203.0.113.45 | |||
| - type: AAAA | |||
| value: 2001:db8::46 | |||
| nadcbiqkbgq._companyname: | |||
| type: TXT | |||
| value: a-different-proof-of-ownership | |||
| pointer: | |||
| type: CNAME | |||
| value: look.over-here.net. | |||
| www: | |||
| - type: A | |||
| values: | |||
| - 203.0.113.42 | |||
| - 203.0.113.43 | |||
| - type: AAAA | |||
| value: 2001:db8::44 | |||
| @ -0,0 +1,4 @@ | |||
| --- | |||
| ? '' | |||
| : type: TXT | |||
| value: v=spf1 -all | |||
| @ -0,0 +1,29 @@ | |||
| version: "3.9" # optional since v1.27.0 | |||
| volumes: | |||
| powerdns-db-data: | |||
| name: powerdns-db-data | |||
| services: | |||
| db: | |||
| image: mariadb:10.1 | |||
| ports: | |||
| - "3306:3306" | |||
| environment: | |||
| MYSQL_ROOT_PASSWORD: l3tmein | |||
| volumes: | |||
| - powerdns-db-data:/var/lib/mysql | |||
| powerdns: | |||
| image: psitrax/powerdns | |||
| ports: | |||
| - "53:53/tcp" | |||
| - "53:53/udp" | |||
| - "8081:8081" | |||
| environment: | |||
| MYSQL_HOST: db | |||
| MYSQL_PORT: 3306 | |||
| MYSQL_USER: root | |||
| MYSQL_PASS: l3tmein | |||
| depends_on: | |||
| - db | |||
| command: --api=yes --api-key=its@secret --loglevel=99 --webserver=yes --webserver-address=0.0.0.0 --webserver-allow-from=0.0.0.0/0 --webserver-password=its@secret --webserver-port=8081 --enable-lua-records=shared --edns-subnet-processing=yes | |||
| @ -0,0 +1 @@ | |||
| export POWERDNS_API_KEY="its@secret" | |||
| @ -0,0 +1,271 @@ | |||
| ## Migrating to octoDNS via octodns-dump | |||
| Importing an existing DNS setup into octoDNS management is a very | |||
| straightforward process and can generally be completed in minutes. | |||
| Some relevant documentation for this example is in comments in the YAML | |||
| configuration files. | |||
| * [config/octodns.yaml](config/octodns.yaml) | |||
| * [populate/octodns.yaml](populate/octodns.yaml) | |||
| * [populate/my-dumpable.com.yaml](populate/my-dumpable.com.yaml) | |||
| * [populate/unused-dumpable.com.yaml](populate/unused-dumpable.com.yaml) | |||
| From here on this README focuses on the process of using `octodns-dump` to | |||
| import your existing DNS data into octoDNS. | |||
| ## Checking out the code and setting up the environment | |||
| You would not normally need to check out octoDNS itself, you instead would have | |||
| a git repo with only your configuration files. Here we're cloning the repo only | |||
| to get a copy of the example files. | |||
| ```console | |||
| $ git clone https://github.com/octodns/octodns.git | |||
| $ cd octodns/examples/basic/ | |||
| $ python3 -mvenv env | |||
| $ source ../env.sh | |||
| $ source env/bin/activate | |||
| (env) $ pip install -r requirements.txt | |||
| ``` | |||
| If you were instead creating a repo for your config from scratch it might look | |||
| something like: | |||
| ```console | |||
| $ mkdir octodnsed | |||
| $ cd octodnsed | |||
| $ git init -b main | |||
| $ mkdir config | |||
| # Using your editor of choice create the main config file with your customized | |||
| # version of things, examples/basic/config/octodns.yaml is a good place to | |||
| # start. | |||
| $ vim config/octodns.yaml | |||
| # follow the process here and once complete | |||
| $ git add config | |||
| $ git commit -m "Importting existing records into octoDNS" | |||
| $ git push -u origin main | |||
| ``` | |||
| ## Populating some data that we can later dump | |||
| We need zone & record data in our provider in order to have something to dump | |||
| out into a YAML config, thus our first step is to populate PowerDNS. This is | |||
| just to have something to work with the the actual process that begins in the | |||
| next step. It's not something you'd normally do when migrating to octoDNS. | |||
| Step 0 is to get a local PowerDNS instance running. | |||
| Check out out [Running PowerDNS](../README.md#running-powerdns) for info on | |||
| starting that up. Once you've done that run the following. You can ignore the | |||
| output and move on to the next step. | |||
| ```console | |||
| (env) $ octodns-sync --config-file=populate/octodns.yaml --doit | |||
| ``` | |||
| ## Running octodns-dump | |||
| Once you have your configuration files and octoDNS installed you're ready to | |||
| dump your zone configs. Here we've assumed that the provider being used supports | |||
| `list_zones`, not all do. If you get an error to that effect see | |||
| [dynamic-zone-config](../dynamic-zone-config) for details on how to explicitly | |||
| list your zones in the config file. | |||
| We first tell octodns-dump where to find our config file. We then tell it that | |||
| we want to use the output provider defined in that file named `config` rather | |||
| than having a default one auto-created. This would allow us to customize the | |||
| details of the provider, e.g. things like the `default_ttl`. | |||
| We'll then tell dump where we would like it to write our zone files, in this | |||
| case we want it to put them into `config` provider's directory `./config`. | |||
| The quoted * tells octodns-dump to dynamically source the list of zones and | |||
| dump config files for all of them. If you would only like to dump a specific | |||
| zone you can replace it with the zone name, e.g. `my-domain.com.`, making sure | |||
| to include the trailing `.` | |||
| The final argument passed to `octodns-dump` is the source of data you'd like to | |||
| dump. Here that's our existing provider `powerdns`. | |||
| Note that if you're previously run other examples you may see entries in the | |||
| log output for those zones in addition to the ones we populated for this | |||
| exercise. They shouldn't cause any problems and can be ignored. | |||
| ```console | |||
| (env) $ octodns-dump --config-file=config/octodns.yaml --output-provider config --output-dir ./config '*' powerdns | |||
| 2023-09-12T09:39:59 [4580544000] INFO Manager __init__: config_file=config/octodns.yaml, (octoDNS 1.0.0+bca53089) | |||
| 2023-09-12T09:39:59 [4580544000] INFO Manager _config_executor: max_workers=1 | |||
| 2023-09-12T09:39:59 [4580544000] INFO Manager _config_include_meta: include_meta=False | |||
| 2023-09-12T09:39:59 [4580544000] INFO Manager _config_auto_arpa: auto_arpa=False | |||
| 2023-09-12T09:39:59 [4580544000] INFO Manager __init__: global_processors=[] | |||
| 2023-09-12T09:39:59 [4580544000] INFO Manager __init__: provider=config (octodns.provider.yaml 1.0.0+bca53089) | |||
| 2023-09-12T09:40:00 [4580544000] INFO Manager __init__: provider=powerdns (octodns_powerdns 0.0.4+3189e9c2) | |||
| 2023-09-12T09:40:00 [4580544000] INFO Manager dump: zone=*, output_dir=./config, output_provider=config, lenient=False, split=False, sources=['powerdns'] | |||
| 2023-09-12T09:40:00 [4580544000] INFO Manager dump: using specified output_provider=config | |||
| 2023-09-12T09:40:00 [4580544000] INFO Manager sync: dynamic zone=*, sources=[PowerDnsProvider] | |||
| 2023-09-12T09:40:00 [4580544000] INFO Manager sync: adding dynamic zone=my-dumpable.com. | |||
| 2023-09-12T09:40:00 [4580544000] INFO Manager sync: adding dynamic zone=unused-domain.io. | |||
| Traceback (most recent call last): | |||
| File "/Users/ross/octodns/octodns/examples/migrating-to-octodns/env/bin/octodns-dump", line 8, in <module> | |||
| sys.exit(main()) | |||
| ^^^^^^ | |||
| File "/Users/ross/octodns/octodns/examples/migrating-to-octodns/env/lib/python3.11/site-packages/octodns/cmds/dump.py", line 51, in main | |||
| manager.dump( | |||
| File "/Users/ross/octodns/octodns/examples/migrating-to-octodns/env/lib/python3.11/site-packages/octodns/manager.py", line 868, in dump | |||
| source.populate(zone, lenient=lenient) | |||
| File "/Users/ross/octodns/octodns/examples/migrating-to-octodns/env/lib/python3.11/site-packages/octodns_powerdns/__init__.py", line 433, in populate | |||
| record = Record.new( | |||
| ^^^^^^^^^^^ | |||
| File "/Users/ross/octodns/octodns/examples/migrating-to-octodns/env/lib/python3.11/site-packages/octodns/record/base.py", line 76, in new | |||
| raise ValidationError(fqdn, reasons, context) | |||
| octodns.record.exception.ValidationError: Invalid record "sshfp.my-dumpable.com." | |||
| - unrecognized algorithm "42" | |||
| - unrecognized algorithm "43" | |||
| ``` | |||
| We've intentionally configured data in the provider without following | |||
| (octoDNS's) best practices as it's common to run across these sorts of things | |||
| when migrating. In this case there is a SSHFP record with invalid `algorithm`s. | |||
| Regardless of whether or not you hit errors it is important to carefully look | |||
| over the output of octodns-dump to ensure that it was able to make sense of and | |||
| import everything it found. It usually can, but you may sometimes run across | |||
| things that it can't make sense of or otherwise doesn't support. | |||
| This is especially true if you have any advanced records configured in your | |||
| provider. octoDNS generally cannot convert records with functionality like | |||
| weights and/or geo-coding enabled. It'll generally import a "simple" version of | |||
| that record and indicate it's doing so with a WARNING log message. If you hit | |||
| this situation see [Dynamic Records](/docs/dynamic_records.md). | |||
| ## Running octodns-dump again, now with --lenient | |||
| Back to our example, the invalid `algorithm`s aren't a huge deal and octoDNS | |||
| can be asked to ignore them with the `--lenient` argument. | |||
| ```console | |||
| (env) $ octodns-dump --config-file=config/octodns.yaml --output-provider config --output-dir ./config '*' powerdns --lenient | |||
| 2023-09-12T09:47:48 [4566433280] INFO Manager __init__: config_file=config/octodns.yaml, (octoDNS 1.0.0+bca53089) | |||
| 2023-09-12T09:47:48 [4566433280] INFO Manager _config_executor: max_workers=1 | |||
| 2023-09-12T09:47:48 [4566433280] INFO Manager _config_include_meta: include_meta=False | |||
| 2023-09-12T09:47:48 [4566433280] INFO Manager _config_auto_arpa: auto_arpa=False | |||
| 2023-09-12T09:47:48 [4566433280] INFO Manager __init__: global_processors=[] | |||
| 2023-09-12T09:47:48 [4566433280] INFO Manager __init__: provider=config (octodns.provider.yaml 1.0.0+bca53089) | |||
| 2023-09-12T09:47:48 [4566433280] INFO Manager __init__: provider=powerdns (octodns_powerdns 0.0.4+3189e9c2) | |||
| 2023-09-12T09:47:48 [4566433280] INFO Manager dump: zone=*, output_dir=./config, output_provider=config, lenient=True, split=False, sources=['powerdns'] | |||
| 2023-09-12T09:47:48 [4566433280] INFO Manager dump: using specified output_provider=config | |||
| 2023-09-12T09:47:48 [4566433280] INFO Manager sync: dynamic zone=*, sources=[PowerDnsProvider] | |||
| 2023-09-12T09:47:48 [4566433280] INFO Manager sync: adding dynamic zone=my-dumpable.com. | |||
| 2023-09-12T09:47:48 [4566433280] INFO Manager sync: adding dynamic zone=unused-dumpable.com. | |||
| 2023-09-12T09:47:48 [4566433280] WARNING Record Invalid record "sshfp.my-dumpable.com." | |||
| - unrecognized algorithm "42" | |||
| - unrecognized algorithm "43" | |||
| 2023-09-12T09:47:48 [4566433280] INFO PowerDnsProvider[powerdns] populate: found 8 records, exists=True | |||
| 2023-09-12T09:47:48 [4566433280] INFO YamlProvider[config] plan: desired=my-dumpable.com. | |||
| 2023-09-12T09:47:48 [4566433280] INFO YamlProvider[config] plan: Creates=8, Updates=0, Deletes=0, Existing Records=0 | |||
| 2023-09-12T09:47:48 [4566433280] INFO YamlProvider[config] apply: making 8 changes to my-dumpable.com. | |||
| 2023-09-12T09:47:48 [4566433280] INFO PowerDnsProvider[powerdns] populate: found 1 records, exists=True | |||
| 2023-09-12T09:47:49 [4566433280] INFO YamlProvider[config] plan: desired=unused-dumpable.com. | |||
| 2023-09-12T09:47:49 [4566433280] WARNING YamlProvider[config] root NS record supported, but no record is configured for unused-dumpable.com. | |||
| 2023-09-12T09:47:49 [4566433280] INFO YamlProvider[config] plan: Creates=1, Updates=0, Deletes=0, Existing Records=0 | |||
| 2023-09-12T09:47:49 [4566433280] INFO YamlProvider[config] apply: making 1 changes to unused-dumpable.com. | |||
| ``` | |||
| With `--lenient` we now see a warning about the validation problems, but | |||
| octoDNS does its best to continue anyway. | |||
| ## Examining the results | |||
| We can now take a look in the config/ directory to see the created zone files | |||
| along side our main config. | |||
| ```console | |||
| (env) ls -1 config/ | |||
| my-dumpable.com.yaml | |||
| octodns.yaml | |||
| unused-dumpable.com.yaml | |||
| ``` | |||
| Again note if you're run through other examples you may see more files for | |||
| their zones in this directory. They can be ignored for the purpose of the | |||
| example, but they will be imported and managed nonetheless. | |||
| If you open up the my-dumpable.com.yaml file you'll note that the SSHFP | |||
| record's values have 42 and 43 for their algorithm field. | |||
| With those values if you tried to generate a plan with `octodns-sync` now it | |||
| would fail with validation errors. | |||
| When migrating it's recommended to make as few changes to your config as | |||
| possible initially, that is you want to bring the setup as-is under octoDNS | |||
| management. This is the safest approach, change as little as possible and | |||
| incrementally work towards having a fully managed and compliant/best practice | |||
| config. | |||
| So for now we'll enable `lenient` on the SSHFP record by editing | |||
| my-dumpable.com.yaml adding `octodns.lenient = true` as shown below. For more | |||
| details on see [lenience](/docs/records.md#lenience). | |||
| ```yaml | |||
| ... | |||
| sshpf: | |||
| # TODO: look into bringing this record into compliance | |||
| octodns: | |||
| lenient: true | |||
| type: SSHFP | |||
| values: | |||
| - algorithm: 42 | |||
| fingerprint: abcdef1234567890 | |||
| fingerprint_type: 1 | |||
| - algorithm: 43 | |||
| fingerprint: abcdef1234567890 | |||
| fingerprint_type: 1 | |||
| ... | |||
| ``` | |||
| ## Generating our first plan | |||
| We can now ask octoDNS to create a plan to see if anything would change. Since | |||
| we've looked closely at the log output zone configuration files we have a pretty | |||
| good idea the answer is no. | |||
| ```console | |||
| (env) $ octodns-sync --config-file=config/octodns.yaml | |||
| ... | |||
| 2023-09-12T10:04:27 [4608876032] INFO PowerDnsProvider[powerdns] plan: desired=unused-dumpable.com. | |||
| 2023-09-12T10:04:27 [4608876032] INFO PowerDnsProvider[powerdns] populate: found 1 records, exists=True | |||
| 2023-09-12T10:04:27 [4608876032] WARNING PowerDnsProvider[powerdns] root NS record supported, but no record is configured for unused-dumpable.com. | |||
| 2023-09-12T10:04:27 [4608876032] INFO PowerDnsProvider[powerdns] plan: No changes | |||
| 2023-09-12T10:04:27 [4608876032] INFO YamlProvider[config] populate: found 5 records, exists=False | |||
| 2023-09-12T10:04:27 [4608876032] INFO PowerDnsProvider[powerdns] plan: desired=exxampled.com. | |||
| 2023-09-12T10:04:27 [4608876032] INFO PowerDnsProvider[powerdns] populate: found 5 records, exists=True | |||
| 2023-09-12T10:04:27 [4608876032] INFO PowerDnsProvider[powerdns] plan: No changes | |||
| 2023-09-12T10:04:27 [4608876032] INFO Plan | |||
| ******************************************************************************** | |||
| No changes were planned | |||
| ******************************************************************************** | |||
| ``` | |||
| Most of the output above is omitted, this is the final few lines showing that | |||
| no changes need to be made to get `powerdns` to match `config`. It does have a | |||
| warning about the lack of root NS records in `unused-dumpable.com.`, but we'll | |||
| ignore that in this example. | |||
| If you had unsupported record types or advanced features in use this may not be | |||
| the case and you may see changes. It's completely safe to generate a plan, | |||
| octoDNS won't make any of the changes listed. | |||
| If you see things in the plan output section it's time to triple check and make | |||
| sure they're, hopefully minimal, modifications your OK with making. | |||
| If the changes are due to advanced functionality you'll need to step back and | |||
| plan a careful migration over to [Dynamic Records](/docs/dynamic_records.md) | |||
| which is beyond the scope of this example. | |||
| ## What's Next | |||
| So now you can commit your config and start managing you DNS with octoDNS rather | |||
| than clicking buttons in UIs or using whatever you previous had used. | |||
| * Check out [octoDNS basic example](../basic) for an example of how to create zone configuration YAML files from your existing provider's configuration | |||
| * For a complete list check out the [Examples Directory](../) | |||
| @ -0,0 +1,31 @@ | |||
| --- | |||
| providers: | |||
| # See the basic example for descriptions of the basics of this config file, | |||
| # here we'll focus on things related to octodns-dump. | |||
| # This is our config provider, it sources YAML from disk, but to start we | |||
| # don't have any files in the ./config directory. We need to dump what's in | |||
| # our provider, in this case a local PowerDNS setup, into this config | |||
| # directory as a starting point. | |||
| config: | |||
| class: octodns.provider.yaml.YamlProvider | |||
| directory: ./config | |||
| # This is our existing provider, the one we want to bring under octoDNS | |||
| # management. This example will walk through dumping the existing data in | |||
| # this provider into the config provider's directory above. At that point we | |||
| # should be ready to start managing things with octoDNS going forward. | |||
| powerdns: | |||
| class: octodns_powerdns.PowerDnsProvider | |||
| host: 127.0.0.1 | |||
| port: 8081 | |||
| api_key: env/POWERDNS_API_KEY | |||
| timeout: 10 | |||
| zones: | |||
| '*': | |||
| sources: | |||
| - config | |||
| targets: | |||
| - powerdns | |||
| @ -0,0 +1,41 @@ | |||
| --- | |||
| # Just some records so we'll have something to dump, see the basic example for | |||
| # documentation/descriptions of what's in here. | |||
| '': | |||
| - type: A | |||
| values: | |||
| - 203.0.113.42 | |||
| - 203.0.113.43 | |||
| - type: AAAA | |||
| values: &WEB_AAAA_VALUES | |||
| - 2001:DB8::44 | |||
| - type: NS | |||
| values: | |||
| - ns1.some-provider.com. | |||
| - ns2.some-provider.com. | |||
| - ns3.some-provider.com. | |||
| - ns4.some-provider.com. | |||
| - type: TXT | |||
| values: | |||
| - some-verification=3becb991-932f-4433-a280-9df6f39b6194 | |||
| - z-other-thing=this proves i have control over this domain | |||
| '*': | |||
| - type: A | |||
| value: 203.0.113.45 | |||
| - type: AAAA | |||
| value: 2001:DB8::46 | |||
| sshfp: | |||
| type: SSHFP | |||
| values: | |||
| # These values won't pass validation. They'll be used to walk through the | |||
| # process of safely bringing an existing config up to octoDNS's | |||
| # recommended best practices. | |||
| - algorithm: 42 | |||
| fingerprint: abcdef1234567890 | |||
| fingerprint_type: 1 | |||
| - algorithm: 43 | |||
| fingerprint: abcdef1234567890 | |||
| fingerprint_type: 1 | |||
| www: | |||
| - type: CNAME | |||
| value: my-dumpable.com. | |||
| @ -0,0 +1,26 @@ | |||
| --- | |||
| providers: | |||
| # This is just a quick and easy way to load some data into PowerDNS that we | |||
| # can dump while working through the example. See the basic example for | |||
| # documentation of what's in here | |||
| config: | |||
| class: octodns.provider.yaml.YamlProvider | |||
| directory: ./populate | |||
| powerdns: | |||
| class: octodns_powerdns.PowerDnsProvider | |||
| host: 127.0.0.1 | |||
| port: 8081 | |||
| api_key: env/POWERDNS_API_KEY | |||
| timeout: 10 | |||
| zones: | |||
| '*': | |||
| # we're disabling strict validation here so we can load up data that won't | |||
| # meet octoDNS's best practices so that we can show | |||
| lenient: true | |||
| sources: | |||
| - config | |||
| targets: | |||
| - powerdns | |||
| @ -0,0 +1,5 @@ | |||
| --- | |||
| '': | |||
| type: TXT | |||
| values: | |||
| - v=spf1 -all | |||
| @ -0,0 +1,3 @@ | |||
| octodns>=1.0.1 | |||
| octodns_powerdns>=0.0.5 | |||
| octodns_spf>=0.0.2 | |||
| @ -1,4 +1,4 @@ | |||
| 'OctoDNS: DNS as code - Tools for managing DNS across multiple providers' | |||
| # TODO: remove __VERSION__ w/2.x | |||
| __version__ = __VERSION__ = '1.2.1' | |||
| __version__ = __VERSION__ = '1.3.0' | |||