├── .github
└── workflows
│ ├── linux.yml
│ ├── openssl-1.0.2.yml
│ └── openssl-1.1.1.yml
├── .gitignore
├── AUTHORS
├── Build.from-git
├── CMakeLists.txt
├── COPYING
├── ChangeLog
├── DISCUSS
├── Dockerfile
├── INSTALL
├── LICENSE
├── Makefile.am
├── NEWS
├── README
├── README.md
├── SECURITY.md
├── TODO.md
├── a2md.xml
├── buildconf
├── cmake
└── modules
│ ├── FindAPACHE.cmake
│ ├── FindAPR.cmake
│ └── FindJANSSON.cmake
├── configure.ac
├── contrib
├── README
├── md_events
│ ├── dns_scripts
│ │ ├── DNS_ROUTE53.md
│ │ ├── GoDaddy-README.txt
│ │ ├── README
│ │ ├── dns_add_challtestsrv
│ │ ├── dns_add_clouddns
│ │ ├── dns_add_cloudflare
│ │ ├── dns_add_dnspod
│ │ ├── dns_add_duckdns
│ │ ├── dns_add_godaddy
│ │ ├── dns_add_joker
│ │ ├── dns_add_lexicon
│ │ ├── dns_add_linode
│ │ ├── dns_add_manual
│ │ ├── dns_add_nsupdate
│ │ ├── dns_add_ovh
│ │ ├── dns_add_pdns-mysql
│ │ ├── dns_del_challtestsrv
│ │ ├── dns_del_clouddns
│ │ ├── dns_del_cloudflare
│ │ ├── dns_del_dnspod
│ │ ├── dns_del_duckdns
│ │ ├── dns_del_godaddy
│ │ ├── dns_del_joker
│ │ ├── dns_del_lexicon
│ │ ├── dns_del_linode
│ │ ├── dns_del_manual
│ │ ├── dns_del_nsupdate
│ │ ├── dns_del_ovh
│ │ ├── dns_del_pdns-mysql
│ │ ├── dns_freedns.sh
│ │ ├── dns_godaddy
│ │ └── dns_route53.py
│ └── md_events
└── selinux
│ ├── README
│ ├── mod_md.fc
│ ├── mod_md.if
│ └── mod_md.te
├── docker-compose.yml
├── docker
└── debian-sid
│ ├── Dockerfile
│ └── bin
│ └── mod_md_test.sh
├── docs
└── Testing.md
├── event_interface_notes.txt
├── m4
└── ax_check_compile_flag.m4
├── mod_md_lib
└── main.c
├── mod_md_ocsp_status.png
├── mod_md_status.png
├── patches
├── mod_ssl_md2-2.4.x.diff
├── mod_ssl_md2-trunk.diff
└── mod_ssl_md3-dualcert.patch
├── scripts
├── contributors.sh
├── fix_le_pubcerts.sh
└── md_message.sh
├── src
├── Makefile.am
├── md.h
├── md_acme.c
├── md_acme.h
├── md_acme_acct.c
├── md_acme_acct.h
├── md_acme_authz.c
├── md_acme_authz.h
├── md_acme_drive.c
├── md_acme_drive.h
├── md_acme_order.c
├── md_acme_order.h
├── md_acmev2_drive.c
├── md_acmev2_drive.h
├── md_cmd.h
├── md_cmd_acme.c
├── md_cmd_acme.h
├── md_cmd_main.c
├── md_cmd_reg.c
├── md_cmd_reg.h
├── md_cmd_store.c
├── md_cmd_store.h
├── md_core.c
├── md_crypt.c
├── md_crypt.h
├── md_curl.c
├── md_curl.h
├── md_event.c
├── md_event.h
├── md_http.c
├── md_http.h
├── md_json.c
├── md_json.h
├── md_jws.c
├── md_jws.h
├── md_log.c
├── md_log.h
├── md_ocsp.c
├── md_ocsp.h
├── md_reg.c
├── md_reg.h
├── md_result.c
├── md_result.h
├── md_status.c
├── md_status.h
├── md_store.c
├── md_store.h
├── md_store_fs.c
├── md_store_fs.h
├── md_tailscale.c
├── md_tailscale.h
├── md_time.c
├── md_time.h
├── md_util.c
├── md_util.h
├── md_version.h
├── md_version.h.in
├── mod_md.c
├── mod_md.h
├── mod_md_config.c
├── mod_md_config.h
├── mod_md_drive.c
├── mod_md_drive.h
├── mod_md_ocsp.c
├── mod_md_ocsp.h
├── mod_md_os.c
├── mod_md_os.h
├── mod_md_private.h
├── mod_md_status.c
└── mod_md_status.h
└── test
├── .gitignore
├── Makefile.am
├── modules
└── md
│ ├── __init__.py
│ ├── conftest.py
│ ├── data
│ ├── sectigo-demo-root.pem
│ ├── store_migrate
│ │ └── 1.0
│ │ │ └── sample1
│ │ │ ├── accounts
│ │ │ └── ACME-localhost-0000
│ │ │ │ ├── account.json
│ │ │ │ └── account.pem
│ │ │ ├── archive
│ │ │ └── 7007-1502285564.org.1
│ │ │ │ └── md.json
│ │ │ ├── domains
│ │ │ └── 7007-1502285564.org
│ │ │ │ ├── cert.pem
│ │ │ │ ├── chain.pem
│ │ │ │ ├── md.json
│ │ │ │ └── pkey.pem
│ │ │ ├── httpd.json
│ │ │ └── md_store.json
│ ├── test_920
│ │ └── 002.pubcert
│ ├── test_conf_validate
│ │ └── test_014.conf
│ ├── test_drive
│ │ └── test1.example.org.conf
│ └── test_roundtrip
│ │ └── temp.conf
│ ├── dns01.py
│ ├── dns01_v2.py
│ ├── http_challenge_foobar.py
│ ├── md_acme.py
│ ├── md_cert_util.py
│ ├── md_certs.py
│ ├── md_conf.py
│ ├── md_env.py
│ ├── message.py
│ ├── msg_fail_on.py
│ ├── notifail.py
│ ├── notify.py
│ ├── pebble
│ ├── pebble-eab.json.template
│ └── pebble.json.template
│ ├── test_001_store.py
│ ├── test_010_store_migrate.py
│ ├── test_100_reg_add.py
│ ├── test_110_reg_update.py
│ ├── test_120_reg_list.py
│ ├── test_202_acmev2_regs.py
│ ├── test_300_conf_validate.py
│ ├── test_310_conf_store.py
│ ├── test_502_acmev2_drive.py
│ ├── test_602_roundtrip.py
│ ├── test_702_auto.py
│ ├── test_710_profiles.py
│ ├── test_720_wildcard.py
│ ├── test_730_static.py
│ ├── test_740_acme_errors.py
│ ├── test_741_setup_errors.py
│ ├── test_750_eab.py
│ ├── test_751_sectigo.py
│ ├── test_752_zerossl.py
│ ├── test_780_tailscale.py
│ ├── test_790_failover.py
│ ├── test_800_must_staple.py
│ ├── test_801_stapling.py
│ ├── test_810_ec.py
│ ├── test_820_locks.py
│ ├── test_900_notify.py
│ ├── test_901_message.py
│ ├── test_910_cleanups.py
│ └── test_920_status.py
├── pyhttpd
├── __init__.py
├── certs.py
├── conf.py
├── conf
│ ├── httpd.conf.template
│ ├── mime.types
│ ├── stop.conf.template
│ └── test.conf
├── config.ini.in
├── curl.py
├── env.py
├── htdocs
│ ├── alive.json
│ ├── cgi
│ │ ├── echo.py
│ │ ├── echohd.py
│ │ ├── env.py
│ │ ├── files
│ │ │ └── empty.txt
│ │ ├── hecho.py
│ │ ├── hello.py
│ │ ├── mnot164.py
│ │ ├── necho.py
│ │ └── upload.py
│ ├── forbidden.html
│ ├── index.html
│ ├── noh2
│ │ ├── alive.json
│ │ └── index.html
│ ├── test1
│ │ ├── 001.html
│ │ ├── 002.jpg
│ │ ├── 003.html
│ │ ├── 003
│ │ │ └── 003_img.jpg
│ │ ├── 004.html
│ │ ├── 004
│ │ │ └── gophertiles.jpg
│ │ ├── 006.html
│ │ ├── 006
│ │ │ ├── 006.css
│ │ │ ├── 006.js
│ │ │ └── header.html
│ │ ├── 007.html
│ │ ├── 007
│ │ │ └── 007.py
│ │ ├── 009.py
│ │ ├── alive.json
│ │ ├── apache.org-files
│ │ │ ├── ant.jpg
│ │ │ ├── asf_logo.png
│ │ │ ├── async-ads.js
│ │ │ ├── async-ads.js.br
│ │ │ ├── cse.js
│ │ │ ├── css.css
│ │ │ ├── default.css
│ │ │ ├── defaulten.css
│ │ │ ├── defaulten.js
│ │ │ ├── jquery-2.js
│ │ │ ├── jsapi.js
│ │ │ ├── min.css
│ │ │ ├── min.css.br
│ │ │ ├── mrunit.jpg
│ │ │ ├── search_box_icon.png
│ │ │ ├── small-logo.png
│ │ │ ├── styles.css
│ │ │ └── synapse.jpg
│ │ ├── apache.org.html
│ │ └── index.html
│ └── test2
│ │ ├── 006
│ │ └── 006.css
│ │ ├── 10%abnormal.txt
│ │ ├── alive.json
│ │ └── x%2f.test
├── log.py
├── mod_aptest
│ └── mod_aptest.c
├── nghttp.py
└── result.py
├── requirements.txt
└── unit
├── main.c
├── test_common.h
├── test_md_json.c
└── test_md_util.c
/.github/workflows/linux.yml:
--------------------------------------------------------------------------------
1 | # Copyright 2025 Stefan Eissing (https://dev-icing.de)
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 |
16 | name: Linux
17 |
18 | 'on':
19 | push:
20 | branches:
21 | - master
22 | - '*/ci'
23 | paths-ignore:
24 | - '**/*.md'
25 | pull_request:
26 | branches:
27 | - master
28 | paths-ignore:
29 | - '**/*.md'
30 |
31 | concurrency:
32 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
33 | cancel-in-progress: true
34 |
35 | permissions: {}
36 |
37 | env:
38 | MARGS: "-j5"
39 | CFLAGS: "-g"
40 | pebble-version: v2.7.0
41 |
42 | jobs:
43 | linux:
44 | name: ${{ matrix.name }}
45 | runs-on: ubuntu-latest
46 | timeout-minutes: 30
47 | strategy:
48 | fail-fast: false
49 | matrix:
50 | build:
51 | - name: Default
52 | install_packages:
53 | install_steps: pytest pebble
54 |
55 | steps:
56 | - name: 'install prereqs'
57 | run: |
58 | sudo apt-get update -y
59 | sudo apt-get install -y --no-install-suggests --no-install-recommends \
60 | libtool autoconf automake pkgconf apache2 apache2-dev openssl \
61 | curl nghttp2-client libssl-dev libjansson-dev libcurl4-openssl-dev \
62 | ${{ matrix.build.install_packages }}
63 | python3 -m venv $HOME/venv
64 |
65 | - uses: actions/checkout@v4
66 |
67 | - name: 'install test prereqs'
68 | run: |
69 | [ -x "$HOME/venv/bin/activate" ] && source $HOME/venv/bin/activate
70 | python3 -m pip install -r test/requirements.txt
71 |
72 | - name: setup Go
73 | if: contains(matrix.build.install_steps, 'pebble')
74 | uses: actions/setup-go@v5
75 |
76 | - name: install pebble
77 | if: contains(matrix.build.install_steps, 'pebble')
78 | run: |
79 | export PATH=$PATH:$HOME/go/bin
80 | git clone --quiet --depth=1 -b ${{ env.pebble-version }} https://github.com/letsencrypt/pebble/
81 | cd pebble
82 | go install ./cmd/pebble
83 | go install ./cmd/pebble-challtestsrv
84 |
85 | - name: 'configure'
86 | run: |
87 | export PATH=$PATH:$HOME/go/bin
88 | autoreconf -fi
89 | ./configure --enable-werror
90 |
91 | - name: 'build'
92 | run: make V=1
93 |
94 | - name: pytest
95 | if: contains(matrix.build.install_steps, 'pytest')
96 | env:
97 | PYTEST_ADDOPTS: "--color=yes"
98 | run: |
99 | export PATH=$PATH:$HOME/go/bin
100 | [ -x "$HOME/venv/bin/activate" ] && source $HOME/venv/bin/activate
101 | pytest -v
102 |
--------------------------------------------------------------------------------
/.github/workflows/openssl-1.0.2.yml:
--------------------------------------------------------------------------------
1 | # Copyright 2025 Stefan Eissing (https://dev-icing.de)
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 |
16 | name: OpenSSL-1.0.2
17 |
18 | 'on':
19 | push:
20 | branches:
21 | - master
22 | - '*/ci'
23 | paths-ignore:
24 | - '**/*.md'
25 | pull_request:
26 | branches:
27 | - master
28 | paths-ignore:
29 | - '**/*.md'
30 |
31 | concurrency:
32 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
33 | cancel-in-progress: true
34 |
35 | permissions: {}
36 |
37 | env:
38 | MARGS: "-j5"
39 | CFLAGS: "-g"
40 |
41 | jobs:
42 | linux:
43 | name: ${{ matrix.name }}
44 | runs-on: ubuntu-latest
45 | timeout-minutes: 30
46 | strategy:
47 | fail-fast: false
48 | matrix:
49 | build:
50 | - name: openssl 1.0.2
51 | install_packages:
52 | install_steps:
53 |
54 | steps:
55 | - name: 'install prereqs'
56 | run: |
57 | sudo apt-get update -y
58 | sudo apt-get install -y --no-install-suggests --no-install-recommends \
59 | libtool autoconf automake pkgconf apache2 apache2-dev openssl \
60 | curl nghttp2-client libssl-dev libjansson-dev libcurl4-openssl-dev \
61 | ${{ matrix.build.install_packages }}
62 | python3 -m venv $HOME/venv
63 |
64 | - uses: actions/checkout@v4
65 |
66 | - name: 'install test prereqs'
67 | run: |
68 | [ -x "$HOME/venv/bin/activate" ] && source $HOME/venv/bin/activate
69 | python3 -m pip install -r test/requirements.txt
70 |
71 | - name: 'cache openssl'
72 | uses: actions/cache@v4
73 | id: cache-openssl
74 | env:
75 | cache-name: cache-openssl
76 | with:
77 | path: ~/openssl
78 | key: ${{ runner.os }}-build-${{ env.cache-name }}-1.0.2
79 |
80 | - name: 'install openssl'
81 | if: steps.cache-openssl.outputs.cache-hit != 'true'
82 | run: |
83 | curl -LO https://github.com/openssl/openssl/releases/download/OpenSSL_1_0_2/openssl-1.0.2.tar.gz
84 | tar xfz openssl-1.0.2.tar.gz
85 | cd openssl-1.0.2
86 | CFLAGS="-fPIC" ./config --prefix=$HOME/openssl --libdir=lib shared
87 | make
88 | make -j1 install_sw
89 |
90 | - name: 'configure'
91 | # configure without --enable-werror since openssl 1.0.2 will give warnings
92 | run: |
93 | autoreconf -fi
94 | ./configure --enable-werror --with-openssl=$HOME/openssl
95 |
96 | - name: 'build'
97 | run: make V=1
98 |
99 |
--------------------------------------------------------------------------------
/.github/workflows/openssl-1.1.1.yml:
--------------------------------------------------------------------------------
1 | # Copyright 2025 Stefan Eissing (https://dev-icing.de)
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 |
16 | name: OpenSSL-1.1.1
17 |
18 | 'on':
19 | push:
20 | branches:
21 | - master
22 | - '*/ci'
23 | paths-ignore:
24 | - '**/*.md'
25 | pull_request:
26 | branches:
27 | - master
28 | paths-ignore:
29 | - '**/*.md'
30 |
31 | concurrency:
32 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
33 | cancel-in-progress: true
34 |
35 | permissions: {}
36 |
37 | env:
38 | MARGS: "-j5"
39 | CFLAGS: "-g"
40 |
41 | jobs:
42 | linux:
43 | name: ${{ matrix.name }}
44 | runs-on: ubuntu-latest
45 | timeout-minutes: 30
46 | strategy:
47 | fail-fast: false
48 | matrix:
49 | build:
50 | - name: openssl 1.1.1
51 | install_packages:
52 | install_steps:
53 |
54 | steps:
55 | - name: 'install prereqs'
56 | run: |
57 | sudo apt-get update -y
58 | sudo apt-get install -y --no-install-suggests --no-install-recommends \
59 | libtool autoconf automake pkgconf apache2 apache2-dev openssl \
60 | curl nghttp2-client libssl-dev libjansson-dev libcurl4-openssl-dev \
61 | ${{ matrix.build.install_packages }}
62 | python3 -m venv $HOME/venv
63 |
64 | - uses: actions/checkout@v4
65 |
66 | - name: 'install test prereqs'
67 | run: |
68 | [ -x "$HOME/venv/bin/activate" ] && source $HOME/venv/bin/activate
69 | python3 -m pip install -r test/requirements.txt
70 |
71 | - name: 'cache openssl'
72 | uses: actions/cache@v4
73 | id: cache-openssl
74 | env:
75 | cache-name: cache-openssl
76 | with:
77 | path: ~/openssl
78 | key: ${{ runner.os }}-build-${{ env.cache-name }}-1.1.1
79 |
80 | - name: 'install openssl'
81 | if: steps.cache-openssl.outputs.cache-hit != 'true'
82 | run: |
83 | curl -LO https://github.com/openssl/openssl/releases/download/OpenSSL_1_1_1s/openssl-1.1.1s.tar.gz
84 | tar xfz openssl-1.1.1s.tar.gz
85 | cd openssl-1.1.1s
86 | CFLAGS="-fPIC" ./config --prefix=$HOME/openssl --libdir=lib shared
87 | make
88 | make -j1 install_sw
89 |
90 | - name: 'configure'
91 | # configure without --enable-werror since openssl 1.1.1 will give warnings
92 | run: |
93 | autoreconf -fi
94 | ./configure --enable-werror --with-openssl=$HOME/openssl
95 |
96 | - name: 'build'
97 | run: make V=1
98 |
99 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | mod_md.xcodeproj/xcuserdata/*.xcuserdatad
2 | *.xcuserstate
3 | *.o
4 | *.slo
5 | *.lo
6 | *.la
7 | *.pcap
8 | *~
9 | .libs
10 | .configured
11 | .deps
12 | .dirstamp
13 | .pytest_cache
14 | compile
15 | aclocal.m4
16 | autom4te.cache
17 | autoscan.log
18 | config.guess
19 | config.log
20 | config.status
21 | config.sub
22 | config.h
23 | config.h.in
24 | config.h.in~
25 | config.cache
26 | configure
27 | configure.scan
28 | depcomp
29 | httpd.conf
30 | install-sh
31 | libtool
32 | ltmain.sh
33 | missing
34 | stamp-h1
35 | Makefile.in
36 | Makefile
37 | mod_md-*.tar.gz
38 | m4
39 | .cache
40 | src/a2md
41 | a2md.1
42 |
43 | # Automake test-suite artifacts
44 | /test-driver
45 | /test/test-suite.log
46 | /test/unit/**/*.log
47 | /test/unit/**/*.trs
48 | /test/unit/main
49 | wiki
50 | test/conf/std_vhosts.conf
51 | build/
52 | .idea
53 | a2md.1
54 | README.md.bak
55 | .env
56 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | Stefan Eissing
2 | Tests by Michael Köller
3 |
--------------------------------------------------------------------------------
/Build.from-git:
--------------------------------------------------------------------------------
1 | Building mod_md from the git repository
2 |
3 | You can build in Docker using Dockerfile in the git repository.
4 |
5 | If you don't have/don't want to use Docker, the following procedure
6 | will build it outside docker.
7 |
8 | Prerequisites:
9 | o Assumes you have built httpd from source
10 | o Install libjansson
11 | - From your distro - libjansson-dev
12 | - From source: http://www.digip.org/jansson
13 | o Install libcurl-dev (e.g libcurl4-openssl-dev, or https://curl.haxx.se/download.html)
14 |
15 | Procedure:
16 |
17 | git clone https://github.com/icing/mod_md.git
18 |
19 | autoreconf -i
20 | automake
21 | autoconf
22 | ./configure -C --with-apxs=/usr/local/bin/apxs
23 | make
24 | make install
25 |
26 | To use this mod_md, you need to enable it in some
27 | distribution-specific manner.
28 |
29 | For bare httpd, your configuration should include:
30 |
31 | LoadModule ssl_module modules/mod_ssl.so
32 | LoadModule watchdog_module /usr/local/lib/httpd/modules/mod_watchdog.so
33 | LoadModule md_module /usr/local/lib/httpd/modules/mod_md.so
34 |
35 | See the mod_md README for details on configuring mod_md.
36 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Copyright 2020 Contributors to mod_md project
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | PROJECT(mod_md C)
16 | CMAKE_MINIMUM_REQUIRED(VERSION 3.14)
17 |
18 | INCLUDE(CheckSymbolExists)
19 |
20 | IF(WIN32)
21 | SET(CMAKE_C_FLAGS "/O2 /MD /W3 /Zi")
22 | # TODO: Questionable...people might want to override -DCMAKE_C_FLAGS_DEBUG="..."
23 | SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS}")
24 | SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS}")
25 | ELSE()
26 |
27 | ENDIF()
28 |
29 | SET(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
30 |
31 | SET(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/modules)
32 |
33 | FIND_PACKAGE(OpenSSL REQUIRED)
34 | FIND_PACKAGE(CURL REQUIRED)
35 | FIND_PACKAGE(JANSSON REQUIRED)
36 | FIND_PACKAGE(APACHE REQUIRED)
37 | FIND_PACKAGE(APR REQUIRED)
38 |
39 | INCLUDE_DIRECTORIES(${APR_INCLUDE_DIR})
40 | INCLUDE_DIRECTORIES(${APRUTIL_INCLUDE_DIR})
41 | INCLUDE_DIRECTORIES(${APACHE_INCLUDE_DIR})
42 | INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
43 | INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR})
44 | INCLUDE_DIRECTORIES(${JANSSON_INCLUDE_DIR})
45 |
46 | AUX_SOURCE_DIRECTORY(${CMAKE_SOURCE_DIR}/src SRC_LIST)
47 | ADD_LIBRARY(mod_md MODULE ${SRC_LIST})
48 | SET_TARGET_PROPERTIES(mod_md PROPERTIES PREFIX "")
49 | SET_TARGET_PROPERTIES(mod_md PROPERTIES SUFFIX ".so")
50 |
51 | TARGET_LINK_LIBRARIES(mod_md ${APR_LIBRARIES} ${APRUTIL_LIBRARIES} ${APACHE_LIBRARY} ${OPENSSL_LIBRARIES} ${CURL_LIBRARIES} ${JANSSON_LIBRARIES})
52 |
53 | MESSAGE(STATUS "")
54 | MESSAGE(STATUS "")
55 | MESSAGE(STATUS "mod_md configuration summary:")
56 | MESSAGE(STATUS "")
57 | MESSAGE(STATUS " Build type ...................... : ${CMAKE_BUILD_TYPE}")
58 | MESSAGE(STATUS " Install prefix .................. : ${CMAKE_INSTALL_PREFIX}")
59 | MESSAGE(STATUS " C compiler ...................... : ${CMAKE_C_COMPILER}")
60 | MESSAGE(STATUS " APR include directory ........... : ${APR_INCLUDE_DIR}")
61 | MESSAGE(STATUS " APR libraries ................... : ${APR_LIBRARIES}")
62 | MESSAGE(STATUS " APR Util include directory ...... : ${APRUTIL_INCLUDE_DIR}")
63 | MESSAGE(STATUS " APR Util libraries .............. : ${APRUTIL_LIBRARIES}")
64 | MESSAGE(STATUS " OpenSSL include directory ....... : ${OPENSSL_INCLUDE_DIR}")
65 | MESSAGE(STATUS " OpenSSL libraries ............... : ${OPENSSL_LIBRARIES}")
66 | MESSAGE(STATUS " Curl include directory........... : ${CURL_INCLUDE_DIR}")
67 | MESSAGE(STATUS " Curl libraries................... : ${CURL_LIBRARIES}")
68 | MESSAGE(STATUS " Jansson include directory.........: ${JANSSON_INCLUDE_DIR}")
69 | MESSAGE(STATUS " Jansson libraries ............... : ${JANSSON_LIBRARIES}")
70 | MESSAGE(STATUS "")
71 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 |
2 | Please see the file called LICENSE.
3 |
--------------------------------------------------------------------------------
/DISCUSS:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/DISCUSS
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:xenial
2 |
3 | RUN apt update
4 | RUN apt install -y software-properties-common
5 | ENV LC_ALL C.UTF-8
6 | RUN add-apt-repository ppa:ondrej/apache2
7 | RUN apt update
8 | RUN apt install -y apache2 apache2-dev build-essential autoconf make libtool libssl-dev libjansson-dev libcurl4-openssl-dev
9 |
10 | COPY . mod_md
11 | WORKDIR mod_md
12 |
13 | RUN autoreconf -i
14 | RUN automake
15 | RUN autoconf
16 | RUN ./configure --with-apxs=/usr/bin/apxs
17 | RUN make
18 | RUN make install
19 |
20 | RUN echo >/etc/apache2/mods-available/md.load "LoadModule md_module /usr/lib/apache2/modules/mod_md.so"
21 | RUN ln -s ../mods-available/md.load /etc/apache2/mods-enabled/md.load
22 |
23 | VOLUME /etc/apache
24 | WORKDIR /etc/apache
25 |
26 | CMD ["/usr/sbin/apache2", "-d", ".", "-DFOREGROUND"]
27 |
--------------------------------------------------------------------------------
/Makefile.am:
--------------------------------------------------------------------------------
1 | # Copyright 2019 greenbytes GmbH (https://www.greenbytes.de)
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 | SUBDIRS = @BUILD_SUBDIRS@
16 | DIST_SUBDIRS = src test
17 |
18 | ACLOCAL_AMFLAGS = -I m4
19 |
20 | dist_doc_DATA = README README.md LICENSE
21 | EXTRA_DIST = patches a2md.xml
22 |
23 | if BUILD_MANPAGES
24 | man1_MANS = a2md.1
25 |
26 | a2md.1: $(srcdir)/a2md.xml
27 | rm -f a2md.1
28 | $(XMLTO) --skip-validation man $(srcdir)/a2md.xml
29 |
30 | endif
31 |
32 | .PHONY: test
33 |
34 | test: all-recursive
35 | $(MAKE) -C test/ test
36 |
37 | docker-test:
38 | docker-compose build debian-sid
39 | docker-compose run debian-sid
40 |
--------------------------------------------------------------------------------
/NEWS:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/NEWS
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | See README.md
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | This repository lives on the current, released version.
6 |
7 | If you look at a more long term perspective, the recent
8 | versions regularly become part of Apache httpd and are
9 | supported via that release line.
10 |
11 | ## Reporting a Vulnerability
12 |
13 | Security relevant reports are best made to security@httpd.apache.org
14 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 |
2 | See [github issues](https://github.com/icing/mod_md/issues).
3 |
--------------------------------------------------------------------------------
/buildconf:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # setup/re-init the autoconf files on a raw checkout
4 | # minimalistic version
5 | #
6 |
7 | fail() {
8 | echo "FAIL: $@" >&2
9 | exit 1
10 | }
11 |
12 | # check location
13 |
14 | if [ ! -f ./configure.ac ]; then
15 | fail "$0 needs to run fromt the top level directory where configure.ac resides."
16 | fi
17 |
18 | # check tools we need
19 |
20 | AUTOCONF="${AUTOCONF:-autoconf}"
21 | AUTORECONF="${AUTORECONF:-autoreconf}"
22 | AUTOMAKE="${AUTOMAKE:-automake}"
23 |
24 | for tool in "$AUTOCONF" "$AUTORECONF" "$AUTOMAKE"; do
25 | type "$tool" 2>&1 >/dev/null
26 | if test $? -ne 0; then
27 | fail "need ${tool} installed."
28 | fi
29 | done
30 |
31 | for i in .configured .deps compile aclocal.m4 autom4te.cache \
32 | autoscan.log config.guess config.status config.sub \
33 | config.h config.h.in config.h.in~ configure configure.scan \
34 | depcomp install-sh libtool ltmain.sh missing stamp-h1 \
35 | Makefile.in Makefile \
36 | src/Makefile.in src/Makefile \
37 | test/Makefile.in test/Makefile \
38 | ; do
39 | test -z "$i" || rm -rf "$i"
40 | done
41 |
42 | "$AUTORECONF" -i || exit $?
43 | "$AUTOMAKE" || exit $?
44 | "$AUTOCONF" || exit $?
45 |
--------------------------------------------------------------------------------
/cmake/modules/FindAPACHE.cmake:
--------------------------------------------------------------------------------
1 | # Copyright 2020 Contributors to mod_md project
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | # One must set a hint: APACHE_ROOT_DIR
16 | # Module defines
17 | # APACHE_FOUND - System has APACHE
18 | # APACHE_INCLUDE_DIR - The APACHE include directory
19 | # APACHE_LIBRARIES - Library
20 | # Defined:
21 | # APACHE_LIBRARY - httpd lib
22 |
23 | IF(NOT APACHE_ROOT_DIR)
24 | message(FATAL_ERROR "APACHE_ROOT_DIR is not set. We don't know where to look for APACHE, quitting.")
25 | ENDIF()
26 | FIND_PATH(APACHE_INCLUDE_DIR httpd.h
27 | "${APACHE_ROOT_DIR}/include"
28 | )
29 | SET(APACHE_NAMES ${APACHE_NAMES} httpd libhttpd)
30 | FIND_LIBRARY(APACHE_LIBRARY NAMES ${APACHE_NAMES} PATHS "${APACHE_ROOT_DIR}/lib" "${APACHE_ROOT_DIR}/lib64")
31 | IF (APACHE_LIBRARY AND APACHE_INCLUDE_DIR)
32 | SET(APACHE_LIBRARIES ${APACHE_LIBRARY})
33 | SET(APACHE_FOUND "YES")
34 | ELSE (APACHE_LIBRARY AND APACHE_INCLUDE_DIR)
35 | SET(APACHE_FOUND "NO")
36 | ENDIF (APACHE_LIBRARY AND APACHE_INCLUDE_DIR)
37 |
38 | IF (APACHE_FOUND)
39 | IF (NOT APACHE_FIND_QUIETLY)
40 | MESSAGE(STATUS "Found APACHE: ${APACHE_LIBRARIES}")
41 | ENDIF (NOT APACHE_FIND_QUIETLY)
42 | ELSE (APACHE_FOUND)
43 | IF (APACHE_FIND_REQUIRED)
44 | MESSAGE(FATAL_ERROR "Could not find APACHE library")
45 | ENDIF (APACHE_FIND_REQUIRED)
46 | ENDIF (APACHE_FOUND)
47 |
48 | MARK_AS_ADVANCED(
49 | APACHE_LIBRARY
50 | APACHE_INCLUDE_DIR
51 | )
--------------------------------------------------------------------------------
/cmake/modules/FindAPR.cmake:
--------------------------------------------------------------------------------
1 | # Copyright 2020 Contributors to mod_md project
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | # One must set a hint: APR_ROOT_DIR, APRUTIL_ROOT_DIR
16 | # Find the APR includes and libraries
17 | # Module defines
18 | # APR_INCLUDE_DIR and APRUTIL_INCLUDE_DIR, apr.h, etc.
19 | # APR_LIBRARIES and APRUTIL_LIBRARIES, libs needed to use APR.
20 | # APR_FOUND and APRUTIL_FOUND, whether APR usable.
21 | # Defined:
22 | # APR_LIBRARY and APRUTIL_LIBRARY, where to find the APR library.
23 |
24 | IF(NOT APR_ROOT_DIR)
25 | message(FATAL_ERROR "APR_ROOT_DIR is not set. We don't know where to look for APR, quitting.")
26 | ENDIF()
27 |
28 | IF(NOT APRUTIL_ROOT_DIR)
29 | message(FATAL_ERROR "APR_ROOT_DIR is not set. We don't know where to look for APR, quitting.")
30 | ENDIF()
31 |
32 | FIND_PATH(APR_INCLUDE_DIR apr.h
33 | "${APR_ROOT_DIR}/include"
34 | )
35 | SET(APR_NAMES ${APR_NAMES} libapr-1 apr-1)
36 | FIND_LIBRARY(APR_LIBRARY NAMES ${APR_NAMES} PATHS "${APR_ROOT_DIR}/lib" "${APR_ROOT_DIR}/lib64")
37 | IF (APR_LIBRARY AND APR_INCLUDE_DIR)
38 | SET(APR_LIBRARIES ${APR_LIBRARY})
39 | SET(APR_FOUND "YES")
40 | ELSE (APR_LIBRARY AND APR_INCLUDE_DIR)
41 | SET(APR_FOUND "NO")
42 | ENDIF (APR_LIBRARY AND APR_INCLUDE_DIR)
43 |
44 | IF (APR_FOUND)
45 | IF (NOT APR_FIND_QUIETLY)
46 | MESSAGE(STATUS "Found APR: ${APR_LIBRARIES}")
47 | ENDIF (NOT APR_FIND_QUIETLY)
48 | ELSE (APR_FOUND)
49 | IF (APR_FIND_REQUIRED)
50 | MESSAGE(FATAL_ERROR "Could not find APR library")
51 | ENDIF (APR_FIND_REQUIRED)
52 | ENDIF (APR_FOUND)
53 |
54 | MARK_AS_ADVANCED(
55 | APR_LIBRARY
56 | APR_INCLUDE_DIR
57 | )
58 |
59 | # APR Util
60 | FIND_PATH(APRUTIL_INCLUDE_DIR apu.h
61 | "${APRUTIL_ROOT_DIR}/include"
62 | )
63 |
64 | SET(APRUTIL_NAMES ${APRUTIL_NAMES} libaprutil-1 aprutil-1)
65 | FIND_LIBRARY(APRUTIL_LIBRARY
66 | NAMES ${APRUTIL_NAMES}
67 | PATHS "${APRUTIL_ROOT_DIR}/lib" "${APRUTIL_ROOT_DIR}/lib64"
68 | )
69 |
70 | IF (APRUTIL_LIBRARY AND APRUTIL_INCLUDE_DIR)
71 | SET(APRUTIL_LIBRARIES ${APRUTIL_LIBRARY})
72 | SET(APRUTIL_FOUND "YES")
73 | ELSE (APRUTIL_LIBRARY AND APRUTIL_INCLUDE_DIR)
74 | SET(APRUTIL_FOUND "NO")
75 | ENDIF (APRUTIL_LIBRARY AND APRUTIL_INCLUDE_DIR)
76 |
77 | IF (APRUTIL_FOUND)
78 | IF (NOT APRUTIL_FIND_QUIETLY)
79 | MESSAGE(STATUS "Found APRUTIL: ${APRUTIL_LIBRARIES}")
80 | ENDIF (NOT APRUTIL_FIND_QUIETLY)
81 | ELSE (APRUTIL_FOUND)
82 | IF (APRUTIL_FIND_REQUIRED)
83 | MESSAGE(FATAL_ERROR "Could not find APRUTIL library")
84 | ENDIF (APRUTIL_FIND_REQUIRED)
85 | ENDIF (APRUTIL_FOUND)
86 |
87 | MARK_AS_ADVANCED(
88 | APRUTIL_LIBRARY
89 | APRUTIL_INCLUDE_DIR
90 | )
91 |
--------------------------------------------------------------------------------
/cmake/modules/FindJANSSON.cmake:
--------------------------------------------------------------------------------
1 | # Copyright 2020 Contributors to mod_md project
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | # One must set a hint: JANSSON_ROOT_DIR
16 | # Once done this will define
17 | # JANSSON_FOUND
18 | # JANSSON_INCLUDE_DIRS
19 | # JANSSON_LIBRARIES
20 |
21 | IF(NOT JANSSON_ROOT_DIR)
22 | message(FATAL_ERROR "JANSSON_ROOT_DIR is not set. We don't know where to look for JANSSON, quitting.")
23 | ENDIF()
24 |
25 |
26 | FIND_PATH(JANSSON_INCLUDE_DIR jansson.h
27 | "${JANSSON_ROOT_DIR}/include"
28 | )
29 |
30 | SET(JANSSON_NAMES ${JANSSON_NAMES} jansson libjansson)
31 | FIND_LIBRARY(JANSSON_LIBRARY NAMES ${JANSSON_NAMES} PATHS "${JANSSON_ROOT_DIR}/lib" "${JANSSON_ROOT_DIR}/lib64")
32 |
33 | IF (JANSSON_LIBRARY AND JANSSON_INCLUDE_DIR)
34 | SET(JANSSON_LIBRARIES ${JANSSON_LIBRARY})
35 | SET(JANSSON_FOUND "YES")
36 | ELSE (JANSSON_LIBRARY AND JANSSON_INCLUDE_DIR)
37 | SET(JANSSON_FOUND "NO")
38 | ENDIF (JANSSON_LIBRARY AND JANSSON_INCLUDE_DIR)
39 |
40 | IF (JANSSON_FOUND)
41 | IF (NOT JANSSON_FIND_QUIETLY)
42 | MESSAGE(STATUS "Found JANSSON: ${JANSSON_LIBRARIES}")
43 | ENDIF (NOT JANSSON_FIND_QUIETLY)
44 | ELSE (JANSSON_FOUND)
45 | IF (JANSSON_FIND_REQUIRED)
46 | MESSAGE(FATAL_ERROR "Could not find JANSSON library")
47 | ENDIF (JANSSON_FIND_REQUIRED)
48 | ENDIF (JANSSON_FOUND)
49 |
50 | MARK_AS_ADVANCED(
51 | JANSSON_LIBRARY
52 | JANSSON_INCLUDE_DIR
53 | )
54 |
--------------------------------------------------------------------------------
/contrib/README:
--------------------------------------------------------------------------------
1 | This directory contains contributed code that may be useful in
2 | conjunction with mod_md. Any support is from the community.
3 |
4 |
5 |
--------------------------------------------------------------------------------
/contrib/md_events/dns_scripts/DNS_ROUTE53.md:
--------------------------------------------------------------------------------
1 | # Do DNS-01 verification using Route53
2 |
3 | I was not about to implement this in BASH, sorry guys. I'd like you to have it, however.
4 |
5 | It's pretty simple to use.
6 |
7 | 1. pip install boto3 dnspython
8 | 2. ln -s dns_route53.py dns_add_route53
9 | 3. ln -s dns_route53.py dns_del_route53
10 | 4. Use it just like the other scripts
11 |
--------------------------------------------------------------------------------
/contrib/md_events/dns_scripts/GoDaddy-README.txt:
--------------------------------------------------------------------------------
1 | Using GoDaddy DNS for LetsEncrypt domain validation.
2 |
3 | Quick guide to setting up getssl for domain validation of
4 | GoDaddy DNS domains.
5 |
6 | There are two prerequisites to using getssl with GoDaddy DNS:
7 |
8 | 1) Obtain an API access key from developer.godaddy.com
9 | At first sign-up, you will be required to take a "test" key.
10 | This is NOT what you need. Accept it, then get a "Production"
11 | key. At this writing, there is no charge - but you must have
12 | a GoDaddy customer account.
13 |
14 | You must get the API key for the account which owns the domain
15 | that you want to get certificates for. If the domains that you
16 | manage are owned by more than one account, get a key for each.
17 |
18 | The access key consists of a "Key" and a "Secret". You need
19 | both.
20 |
21 | 2) Obtain JSON.sh - https://github.com/dominictarr/JSON.sh
22 |
23 | With those in hand, the installation procedure is:
24 |
25 | 1) Put JSON.sh in the getssl DNS scripts directory
26 | Default: /usr/share/getssl/dns_scripts
27 |
28 | 2) Open your config file (the global file in ~/.getssl/getssl.cfg
29 | or the per-account file in ~/.getssl/example.net/getssl.cfg
30 |
31 | 3) Set the following options:
32 | VALIDATE_VIA_DNS="true"
33 | DNS_ADD_COMMAND="/usr/share/getssl/dns_scripts/dns_add_godaddy"
34 | DNS_DEL_COMMAND="/usr/share/getssl/dns_scripts/dns_del_godaddy"
35 | # The API key for your account/this domain
36 | export GODADDY_KEY="..." GODADDY_SECRET="..."
37 |
38 | 4) Set any other options that you wish (per the standard
39 | directions.) Use the test CA to make sure that
40 | everything is setup correctly.
41 |
42 | That's it. getssl example.net will now validate with DNS.
43 |
44 | To trace record additions and removals, run getssl as
45 | GODADDY_TRACE=Y getssl example.net
46 |
47 | There are additional options, which are documented in the
48 | *godaddy" files and dns_godaddy -h.
49 |
50 | Copyright (2017) Timothe Litt litt at acm _dot org
51 |
52 | This sofware may be freely used providing this notice is included with
53 | all copies. The name of the author may not be used to endorse
54 | any other product or derivative work. No warranty is provided
55 | and the user assumes all responsibility for use of this software.
56 |
57 | Report any issues to https://github.com/tlhackque/getssl/issues.
58 |
59 | Enjoy.
60 |
61 |
--------------------------------------------------------------------------------
/contrib/md_events/dns_scripts/README:
--------------------------------------------------------------------------------
1 | This directory is a snapshot of https://github.com/srvrco/getssl/dns_scripts
2 |
3 | It is provided AS-IS, subject to the licensing of https://github.com/srvrco/getssl.
4 |
5 | An interface is provided that MAY work with these scripts to install dns-01 challenge
6 | responses. It is highly experimental at this time. After installing md_events, read
7 | the example.net.conf description for details.
8 |
9 | While not supported, reports of success (or even failure) are welcome.
10 |
--------------------------------------------------------------------------------
/contrib/md_events/dns_scripts/dns_add_challtestsrv:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Simple script to update the challtestserv mock DNS server when testing DNS responses
3 |
4 | fulldomain="${1}"
5 | token="${2}"
6 |
7 | curl -X POST -d "{\"host\":\"_acme-challenge.${fulldomain}.\", \"value\": \"${token}\"}" http://10.30.50.3:8055/set-txt
8 |
--------------------------------------------------------------------------------
/contrib/md_events/dns_scripts/dns_add_dnspod:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # need to add your email address and key to dnspod below
4 | key=${DNSPOD_API_KEY:-}
5 |
6 | fulldomain="$1"
7 | token="$2"
8 |
9 | NumParts=$(echo "$fulldomain" | awk -F"." '{print NF}')
10 | if [[ $NumParts -gt 2 ]]; then
11 | domain=$(echo "$fulldomain" | awk -F\. '{print $(NF-1) FS $NF}')
12 | txtname="_acme-challenge$(echo "$fulldomain" | awk -F\. '{for (i=1; i/dev/null | grep Auth-Sid | awk '{ print $2 }')
27 |
28 | ## put zone data in tempfile
29 | curl --silent -X POST https://dmapi.joker.com/request/dns-zone-get \
30 | -H "Accept: application/json" -H "User-Agent: getssl/0.1" \
31 | -H "application/x-www-form-urlencoded" -d "domain=${DOMAIN_ROOT}&auth-sid=${SID}" | \
32 | tail -n +7 >"${TMPFILE}"
33 |
34 | ## add txt record
35 | printf "_acme-challenge.%s. TXT 0 \"%s \" 300\n\n" "${FULLDOMAIN}" "${TOKEN}" >>"${TMPFILE}"
36 |
37 | ## generate encoded url data
38 | URLDATA=$(cat "${TMPFILE}" | sed 's/ /%20/g' | sed 's/"/%22/g' | sed ':a;N;$!ba;s/\n/%0A/g')
39 |
40 | ## write new zonefile to joker
41 | curl --silent --output /dev/null "https://dmapi.joker.com/request/dns-zone-put?domain=${DOMAIN_ROOT}&zone=${URLDATA}&auth-sid=${SID}" 2>&1
42 |
43 | ## remove tempfile
44 | rm -f "${TMPFILE}"
45 |
--------------------------------------------------------------------------------
/contrib/md_events/dns_scripts/dns_add_lexicon:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # a simple wrapper for Lexicon - https://github.com/AnalogJ/lexicon - a python script which can
4 | # Manipulate DNS records on various DNS providers in a standardized way.
5 | # You need to define the following environmental variables
6 | # LEXICON_PROVIDER
7 | # Every DNS service and auth flag maps to an Environmental Variable as follows: LEXICON_{DNS Provider Name}_{Auth Type}
8 | # eg LEXICON_CLOUDFLARE_USERNAME and LEXICON_CLOUDFLARE_TOKEN or LEXICON_DIGITALOCEAN_TOKEN
9 |
10 | fulldomain="${1}"
11 | token="${2}"
12 |
13 | lexicon "$LEXICON_PROVIDER" \
14 | create "$fulldomain" TXT \
15 | --name="_acme-challenge.${fulldomain}." \
16 | --content="$token"
17 |
18 | exit
19 |
--------------------------------------------------------------------------------
/contrib/md_events/dns_scripts/dns_add_linode:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | fulldomain="${1}"
4 | token="${2}"
5 | api_url="https://api.linode.com/api/"
6 | api_key=${LINODE_KEY:-''}
7 |
8 | # Verify that required parameters are set
9 | if [[ -z "$fulldomain" ]]; then
10 | echo "DNS script requires full domain name as first parameter"
11 | exit 1
12 | fi
13 | if [[ -z "$token" ]]; then
14 | echo "DNS script requires challenge token as second parameter"
15 | exit 1
16 | fi
17 | if [[ -z "$LINODE_KEY" ]]; then
18 | echo "LINODE_KEY variable not set"
19 | exit 1
20 | fi
21 |
22 | domain_root=$(echo "$fulldomain" | awk -F\. '{print $(NF-1) FS $NF}')
23 | domain=${fulldomain%.$domain_root}
24 | txtname="_acme-challenge.$domain"
25 |
26 | # Get Domain ID
27 | response=$(curl --silent -X POST "$api_url" \
28 | -H "Accept: application/json" -H "User-Agent: getssl/0.1" -H "application/x-www-form-urlencoded" \
29 | -d "api_key=${api_key}&api_action=domain.list" )
30 | domain_id=$(echo "$response" | egrep -o "{\"DOMAIN\":\"$domain_root\".*\"DOMAINID\":([0-9]+)" | egrep -o "[0-9]+$")
31 | if [[ $domain_id == "" ]]; then
32 | echo "Failed to fetch DomainID"
33 | exit 1
34 | fi
35 |
36 | # Create TXT record
37 | response=$(curl --silent -X POST "$api_url" \
38 | -H "Accept: application/json" -H "User-Agent: getssl/0.1" -H "application/x-www-form-urlencoded" \
39 | -d "api_key=$api_key&api_action=domain.resource.create&DomainID=$domain_id&Type=TXT&Name=$txtname&Target=$token" )
40 | errors=$(echo "$response" | egrep -o "\"ERRORARRAY\":\[.*\]")
41 | if [[ $errors != "\"ERRORARRAY\":[]" ]]; then
42 | echo "Something went wrong: $errors"
43 | exit 1
44 | fi
45 |
--------------------------------------------------------------------------------
/contrib/md_events/dns_scripts/dns_add_manual:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "In the DNS, a new TXT record needs to be created for;"
4 | echo "_acme-challenge.${1}"
5 | echo "containing the following value"
6 | echo "$2"
7 |
8 | read -r -p "Press any key to obtain the certificate once the records have been updated..."
9 |
--------------------------------------------------------------------------------
/contrib/md_events/dns_scripts/dns_add_nsupdate:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # example of script to add token to local dns using nsupdate
4 |
5 | fulldomain="$1"
6 | token="$2"
7 |
8 | # VARIABLES:
9 | #
10 | # DNS_NSUPDATE_KEYFILE - path to a TSIG key file, if required
11 | # DNS_NSUPDATE_GETKEY - command to execute if access to the key file requires
12 | # some special action: mounting a disk, decrypting a file..
13 | # Called with the operation 'add' and action 'open" / 'close'
14 |
15 |
16 | if [ -n "${DNS_NSUPDATE_KEYFILE}" ]; then
17 | if [ -n "${DNS_NSUPDATE_KEY_HOOK}" ] && ! ${DNS_NSUPDATE_KEY_HOOK} 'add' 'open' "${fulldomain}" ; then
18 | exit $(( $? + 128 ))
19 | fi
20 |
21 | options="-k ${DNS_NSUPDATE_KEYFILE}"
22 | fi
23 |
24 | if [ -n "${DNS_SERVER}" ]; then
25 | cmd+="server ${DNS_SERVER}\n"
26 | fi
27 |
28 | cmd+="update add ${DNS_ZONE:-"_acme-challenge.${fulldomain}."} 300 in TXT \"${token}\"\n"
29 | cmd+="\n" # blank line is a "send" command to nsupdate
30 |
31 | printf "$cmd" | nsupdate ${options} -v
32 |
33 | sts=$?
34 |
35 | if [ -n "${DNS_NSUPDATE_KEYFILE}" ]; then
36 | if [ -n "${DNS_NSUPDATE_KEY_HOOK}" ] && ! ${DNS_NSUPDATE_KEY_HOOK} 'add' 'close' "${fulldomain}"; then
37 | exit $(( sts + ( $? * 10 ) ))
38 | fi
39 | fi
40 |
41 | exit ${sts}
42 |
--------------------------------------------------------------------------------
/contrib/md_events/dns_scripts/dns_add_ovh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | domains=($(echo "$1"|sed -e 's/^\(\([a-zA-Z0-9.-]*\?\)\.\)*\([a-zA-Z0-9-]\+\.[a-zA-Z-]\+\)$/"\1" _acme-challenge.\2 \3/g'))
4 | challenge="$2"
5 |
6 | # Please, do not forget to ask for your credentials at https://eu.api.ovh.com/createToken/
7 | # permissions needed are /domain/zone/* in GET,POST,DELETE
8 | applicationKey="YourAK"
9 | applicationSecret="YourAS"
10 | consumerKey="YourCK"
11 |
12 | topDomain=${domains[2]}
13 | subDomain=${domains[1]%%.}
14 |
15 | function send
16 | {
17 | method=$1
18 | url=$2
19 | body=$3
20 | ts=$(date +%s)
21 |
22 | sign=\$1\$$(echo -n "${applicationSecret}+${consumerKey}+${method}+https://eu.api.ovh.com/1.0${url}+${body}+${ts}"|sha1sum|cut -d" " -f1)
23 | curl -X "${method}" -H "Content-Type: application/json" -H "X-Ovh-Application: ${applicationKey}" -H "X-Ovh-Timestamp: ${ts}" -H "X-Ovh-Signature: ${sign}" -H "X-Ovh-Consumer: ${consumerKey}" -d "${body}" "https://eu.api.ovh.com/1.0${url}"
24 | }
25 |
26 | # Creation request
27 | send POST "/domain/zone/${topDomain}/record" "{\"fieldType\":\"TXT\",\"subDomain\":\"$subDomain\",\"ttl\":60,\"target\":\"$challenge\"}"
28 |
29 | # Refresh request
30 | send POST "/domain/zone/${topDomain}/refresh" ""
31 |
32 | # Pause for 10 seconds, for DNS propagation
33 | sleep 10
34 |
--------------------------------------------------------------------------------
/contrib/md_events/dns_scripts/dns_add_pdns-mysql:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # You must either have a suitable ~/.my.cnf containing a user / pass
4 | # for your mysql / mariadb database, OR you must uncomment the next line
5 | # (which is a security risk; don't do it!) and adjust accordingly.
6 |
7 | #CREDENTIALS="-uUSERNAME -pPASSWORD"
8 |
9 | FQDN=$1
10 | TOKEN=$2
11 |
12 | # If your database name is not powerdns, change it here.
13 | DB="powerdns"
14 |
15 | DOMAIN=${FQDN}
16 |
17 | # Iterate over the database, checking for a match. Keep stripping
18 | # subdomains off 1 by 1 until we find one, or exit with an error.
19 | while [[ -z "${DOMAIN_ID}" ]]; do
20 | DOMAIN_ID=$(mysql -ss "${CREDENTIALS}" -e "SELECT id FROM ${DB}.domains WHERE name='${DOMAIN}'")
21 | if [[ -z "${DOMAIN_ID}" ]]; then
22 | DOMAIN="$(echo "${DOMAIN}"|cut -d. -f1 --complement)"
23 | fi
24 | if [[ ${DOMAIN} != *"."* ]]; then
25 | echo "Cannot find matching domain record! ABORT!"
26 | exit 1
27 | fi
28 | done
29 |
30 | echo "Domain ID: ${DOMAIN_ID} | FQDN: ${FQDN} | Domain: ${DOMAIN}"
31 |
32 | mysql -ss "${CREDENTIALS}" -e "INSERT INTO ${DB}.records \
33 | (domain_id, name, content, type,ttl,prio) VALUES \
34 | (${DOMAIN_ID},'_acme-challenge.${FQDN}','${TOKEN}','TXT',120,NULL);"
35 |
--------------------------------------------------------------------------------
/contrib/md_events/dns_scripts/dns_del_challtestsrv:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Simple script to update the challtestserv mock DNS server when testing DNS responses
3 |
4 | fulldomain="${1}"
5 |
6 | curl -X POST -d "{\"host\":\"_acme-challenge.${fulldomain}.\"}" http://10.30.50.3:8055/clear-txt
7 |
--------------------------------------------------------------------------------
/contrib/md_events/dns_scripts/dns_del_dnspod:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # need to add your email address and key to dnspod below
4 | key=${DNSPOD_API_KEY:-}
5 |
6 | fulldomain="$1"
7 |
8 | NumParts=$(echo "$fulldomain" | awk -F"." '{print NF}')
9 | if [[ $NumParts -gt 2 ]]; then
10 | domain=$(echo "$fulldomain" | awk -F\. '{print $(NF-1) FS $NF}')
11 | # txtname="_acme-challenge$(echo "$fulldomain" | awk -F\. '{for (i=1; i/dev/null | grep Auth-Sid | awk '{ print $2 }')
27 |
28 | ## put zone data in tempfile
29 | curl --silent -X POST https://dmapi.joker.com/request/dns-zone-get \
30 | -H "Accept: application/json" -H "User-Agent: getssl/0.1" \
31 | -H "application/x-www-form-urlencoded" -d "domain=${DOMAIN_ROOT}&auth-sid=${SID}" | \
32 | tail -n +7 >"${TMPFILE}"
33 |
34 | ## remove txt record
35 | sed -i "/_acme-challenge.${FULLDOMAIN}.*${TOKEN}.*/d" "${TMPFILE}"
36 |
37 | ## generate encoded url data
38 | URLDATA=$(cat "${TMPFILE}" | sed 's/ /%20/g' | sed 's/"/%22/g' | sed ':a;N;$!ba;s/\n/%0A/g')
39 |
40 | ## write new zonefile to joker
41 | curl --silent --output /dev/null "https://dmapi.joker.com/request/dns-zone-put?domain=${DOMAIN_ROOT}&zone=${URLDATA}&auth-sid=${SID}" 2>&1
42 |
43 | ## remove tempfile
44 | rm -f "${TMPFILE}"
45 |
--------------------------------------------------------------------------------
/contrib/md_events/dns_scripts/dns_del_lexicon:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # a simple wrapper for Lexicon - https://github.com/AnalogJ/lexicon - a python script which can
4 | # Manipulate DNS records on various DNS providers in a standardized way.
5 | # You need to define the following environmental variables
6 | # LEXICON_PROVIDER
7 | # Every DNS service and auth flag maps to an Environmental Variable as follows: LEXICON_{DNS Provider Name}_{Auth Type}
8 | # eg LEXICON_CLOUDFLARE_USERNAME and LEXICON_CLOUDFLARE_TOKEN or LEXICON_DIGITALOCEAN_TOKEN
9 |
10 | fulldomain="${1}"
11 | token="${2}"
12 |
13 | lexicon "$LEXICON_PROVIDER" \
14 | delete "$fulldomain" TXT \
15 | --name="_acme-challenge.${fulldomain}." \
16 | --content="$token"
17 |
18 | exit
19 |
--------------------------------------------------------------------------------
/contrib/md_events/dns_scripts/dns_del_linode:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | fulldomain="${1}"
4 | api_url="https://api.linode.com/api/"
5 | api_key=${LINODE_KEY:-''}
6 |
7 | # Verify that required parameters are set
8 | if [[ -z "$fulldomain" ]]; then
9 | echo "DNS script requires full domain name as first parameter"
10 | exit 1
11 | fi
12 | if [[ -z "$LINODE_KEY" ]]; then
13 | echo "LINODE_KEY variable not set"
14 | exit 1
15 | fi
16 |
17 | domain_root=$(echo "$fulldomain" | awk -F\. '{print $(NF-1) FS $NF}')
18 | domain=${fulldomain%.$domain_root}
19 | txtname="_acme-challenge.$domain"
20 |
21 | # Get Domain ID
22 | response=$(curl --silent -X POST "$api_url" \
23 | -H "Accept: application/json" -H "User-Agent: getssl/0.1" -H "application/x-www-form-urlencoded" \
24 | -d "api_key=${api_key}&api_action=domain.list" )
25 | domain_id=$(echo "$response" | egrep -o "{\"DOMAIN\":\"$domain_root\".*\"DOMAINID\":([0-9]+)" | egrep -o "[0-9]+$")
26 | if [[ $domain_id == "" ]]; then
27 | echo "Failed to fetch DomainID"
28 | exit 1
29 | fi
30 |
31 | # Get Resource ID
32 | response=$(curl --silent -X POST "$api_url" \
33 | -H "Accept: application/json" -H "User-Agent: getssl/0.1" -H "application/x-www-form-urlencoded" \
34 | -d "api_key=${api_key}&api_action=domain.resource.list&DomainID=$domain_id" )
35 | resource_id=$(echo "$response" | egrep -o "\"RESOURCEID\":[0-9]+,\"TYPE\":\"TXT\",\"NAME\":\"$txtname\"" | egrep -o "\"RESOURCEID\":[0-9]+" | egrep -o "[0-9]+$")
36 | if [[ $resource_id == "" ]]; then
37 | echo "Failed to fetch ResourceID"
38 | exit 1
39 | fi
40 |
41 | # Delete TXT record
42 | response=$(curl --silent -X POST "$api_url" \
43 | -H "Accept: application/json" -H "User-Agent: getssl/0.1" -H "application/x-www-form-urlencoded" \
44 | -d "api_key=$api_key&api_action=domain.resource.delete&DomainID=$domain_id&ResourceID=$resource_id" )
45 | errors=$(echo "$response" | egrep -o "\"ERRORARRAY\":\[.*\]")
46 | if [[ $errors != "\"ERRORARRAY\":[]" ]]; then
47 | echo "Something went wrong: $errors"
48 | exit 1
49 | fi
50 |
--------------------------------------------------------------------------------
/contrib/md_events/dns_scripts/dns_del_manual:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "In the DNS, the following DNS record should be deleted ;"
4 | echo "_acme-challenge.${1}"
5 |
6 | read -r -p "Press any key to obtain the certificate once the records have been updated..."
7 |
--------------------------------------------------------------------------------
/contrib/md_events/dns_scripts/dns_del_nsupdate:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # example of script to remove token from local dns using nsupdate
4 |
5 | fulldomain="$1"
6 | token="$2"
7 |
8 | # VARIABLES:
9 | #
10 | # DNS_NSUPDATE_KEYFILE - path to a TSIG key file, if required
11 | # DNS_NSUPDATE_GETKEY - command to execute if access to the key file requires
12 | # some special action: dismounting a disk, encrypting a
13 | # file... Called with the operation 'del' and action
14 | # 'open" / 'close'
15 |
16 | if [ -n "${DNS_NSUPDATE_KEYFILE}" ]; then
17 | if [ -n "${DNS_NSUPDATE_KEY_HOOK}" ] && ! "${DNS_NSUPDATE_KEY_HOOK}" 'del' 'open' "${fulldomain}" ; then
18 | exit $(( $? + 128 ))
19 | fi
20 |
21 | options="-k ${DNS_NSUPDATE_KEYFILE}"
22 | fi
23 |
24 | if [ -n "${DNS_SERVER}" ]; then
25 | cmd+="server ${DNS_SERVER}\n"
26 | fi
27 |
28 | cmd+="update delete ${DNS_ZONE:-"_acme-challenge.${fulldomain}."} 300 in TXT \"${token}\"\n"
29 | cmd+="\n" # blank line is a "send" command to nsupdate
30 |
31 | printf "$cmd" | nsupdate ${options} -v
32 |
33 | sts=$?
34 |
35 | if [ -n "${DNS_NSUPDATE_KEYFILE}" ]; then
36 | if [ -n "${DNS_NSUPDATE_KEY_HOOK}" ] && ! "${DNS_NSUPDATE_KEY_HOOK}" 'del' 'close' "${fulldomain}" ; then
37 | exit $(( sts + ( $? * 10 ) ))
38 | fi
39 | fi
40 |
41 | exit ${sts}
42 |
--------------------------------------------------------------------------------
/contrib/md_events/dns_scripts/dns_del_ovh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | domains=($(echo "$1"|sed -e 's/^\(\([a-zA-Z0-9.-]*\?\)\.\)*\([a-zA-Z0-9-]\+\.[a-zA-Z-]\+\)$/"\1" _acme-challenge.\2 \3/g'))
4 | #challenge="$2"
5 |
6 | # Please, do not forget to ask for your credentials at https://eu.api.ovh.com/createToken/
7 | # permissions needed are /domain/zone/* in GET,POST,DELETE
8 | applicationKey="YourAK"
9 | applicationSecret="YourAS"
10 | consumerKey="YourCK"
11 |
12 | topDomain=${domains[2]}
13 | subDomain=${domains[1]%%.}
14 |
15 | function send
16 | {
17 | method=$1
18 | url=$2
19 | body=$3
20 | ts=$(date +%s)
21 |
22 | sign=\$1\$$(echo -n "${applicationSecret}+${consumerKey}+${method}+https://eu.api.ovh.com/1.0${url}+${body}+${ts}"|sha1sum|cut -d" " -f1)
23 | curl -X "${method}" -H "Content-Type: application/json" -H "X-Ovh-Application: ${applicationKey}" -H "X-Ovh-Timestamp: ${ts}" -H "X-Ovh-Signature: ${sign}" -H "X-Ovh-Consumer: ${consumerKey}" -d "${body}" "https://eu.api.ovh.com/1.0${url}"
24 | }
25 |
26 | # Creation request
27 | oldResult=$(send GET "/domain/zone/${topDomain}/record?fieldType=TXT&subDomain=${subDomain}" ""|sed -e 's/\[//' -e 's/\]//')
28 |
29 | for num in ${oldResult//,/ }
30 | do
31 | send DELETE "/domain/zone/${topDomain}/record/${num}" ""
32 | done
33 |
34 | # Refresh request
35 | send POST "/domain/zone/${topDomain}/refresh" ""
36 |
--------------------------------------------------------------------------------
/contrib/md_events/dns_scripts/dns_del_pdns-mysql:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # You must either have a suitable ~/.my.cnf containing a user / pass
4 | # for your mysql / mariadb database, OR you must uncomment the next line
5 | # (which is a security risk; don't do it!) and adjust accordingly.
6 |
7 | #CREDENTIALS="-uUSERNAME -pPASSWORD"
8 |
9 | FQDN=$1
10 |
11 | # If your database name is not powerdns, change it here.
12 | DB="powerdns"
13 |
14 | mysql -ss "${CREDENTIALS}" -e "DELETE FROM ${DB}.records WHERE \
15 | name = '_acme-challenge.${FQDN}';"
16 |
17 | echo "DELETE FROM ${DB}.records WHERE name = '_acme-challenge.${FQDN}';"
18 |
--------------------------------------------------------------------------------
/contrib/md_events/dns_scripts/dns_route53.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import boto3, sys, time
4 | from os.path import basename
5 | import dns.resolver
6 |
7 | client = boto3.client('route53')
8 |
9 | name = sys.argv[0]
10 | fqdn = sys.argv[1]
11 | challenge = sys.argv[2]
12 |
13 | bname = basename(name)
14 | if bname == 'dns_add_route53':
15 | action = 'UPSERT'
16 | elif bname == 'dns_del_route53':
17 | action = 'DELETE'
18 | else:
19 | print("No such action: {a}".format(a=bname))
20 | sys.exit(1)
21 |
22 | try:
23 | response = client.list_hosted_zones()
24 | except Exception as e:
25 | print("Oops: {e!r}".format(e=e))
26 | sys.exit(1)
27 |
28 | zone_id = ""
29 | zone_list = dict()
30 | for zone in response['HostedZones']:
31 | if not zone['Config']['PrivateZone']:
32 | zone_list[zone['Name']] = zone['Id']
33 |
34 | for key in sorted(zone_list.iterkeys(), key=len, reverse=True):
35 | if ".{z}".format(z=key) in ".{z}.".format(z=fqdn):
36 | zone_id = zone_list[key]
37 |
38 | if zone_id == "":
39 | print("We didn't find the zone")
40 | sys.exit(1)
41 |
42 | challenge_fqdn = "_acme-challenge.{f}".format(f=fqdn)
43 | try:
44 | response = client.change_resource_record_sets(
45 | HostedZoneId=zone_id,
46 | ChangeBatch={
47 | 'Comment': 'getssl/Letsencrypt verification',
48 | 'Changes': [
49 | {
50 | 'Action': action,
51 | 'ResourceRecordSet': {
52 | 'Name': challenge_fqdn,
53 | 'Type': 'TXT',
54 | 'TTL': 300,
55 | 'ResourceRecords': [{'Value': "\"{c}\"".format(c=challenge)}]
56 | }
57 | },
58 | ]
59 | }
60 | )
61 | except Exception as e:
62 | print("Oops: {e!r}".format(e=e))
63 | sys.exit(1)
64 |
65 | waiting = 0
66 | if action == 'UPSERT':
67 | # Wait until we see the record before returning. The ACME server's timeout is too short.
68 | # But only if we're adding the record. Don't care how long it takes to delete.
69 | while (True):
70 | try:
71 | my_resolver = dns.resolver.Resolver(configure=False)
72 | my_resolver.nameservers = ['8.8.8.8', '8.8.4.4']
73 | results = my_resolver.query(challenge_fqdn, 'TXT')
74 | data = str(results.response.answer[0][0]).strip('\"')
75 | if data == challenge:
76 | print("found {f} entry".format(f=challenge_fqdn))
77 | else:
78 | print("found {f} entry but it has bad data: {d}".format(f=challenge_fqdn,
79 | d=data))
80 | break
81 |
82 | except dns.resolver.NXDOMAIN:
83 | waiting += 10
84 | print("Didn't find {f} entry yet, sleeping... ({w}s)".format(f=challenge_fqdn,
85 | w=waiting))
86 | time.sleep(10)
87 | pass
88 |
--------------------------------------------------------------------------------
/contrib/selinux/README:
--------------------------------------------------------------------------------
1 | These are the changes to the standard SeLinux policies
2 | necessitated by mod_md as of Fedora 31 (and other distros).
3 |
4 | To install (recent Fedora):
5 | in the directory:
6 | checkmodule -m mod_md.te -o mod_md.mod
7 | semodule_package -m mod_md.mod -o mod_md.pp
8 | semodule -i mod_md.pp
9 |
10 | For older versions, replicy the "module" line with:
11 | policy_module(mod_md, 0.1.1)
12 | Link Makefile to /usr/share/selinux/devel/Makefile
13 | and run make load
14 |
15 | For other distros, check their documentation.
16 |
--------------------------------------------------------------------------------
/contrib/selinux/mod_md.fc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/contrib/selinux/mod_md.fc
--------------------------------------------------------------------------------
/contrib/selinux/mod_md.if:
--------------------------------------------------------------------------------
1 | ##
2 |
--------------------------------------------------------------------------------
/contrib/selinux/mod_md.te:
--------------------------------------------------------------------------------
1 | module mod_md 0.1.1;
2 |
3 | require {
4 | type httpd_t;
5 | type httpd_sys_script_t;
6 | type httpd_sys_content_t;
7 | type httpd_config_t;
8 | type httpd_tmp_t;
9 |
10 | class dir {create getattr setattr read write rename open lock execute add_name remove_name unlink search rmdir};
11 | class file {create getattr setattr read write rename open lock execute execute_no_trans unlink};
12 | class lnk_file { read getattr };
13 | }
14 | # mod_md (by default) maintains its data in /etc/httpd/md. /etc/httpd is labelled httpd_config_t
15 |
16 | allow httpd_t httpd_config_t:dir {create getattr setattr read write rename open lock execute add_name remove_name unlink search rmdir};
17 | allow httpd_t httpd_config_t:file {create getattr setattr read write rename open lock execute execute_no_trans unlink};
18 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.7'
2 | services:
3 | debian-sid:
4 | # build and run tests in a debian sid container
5 | image: ${DOCKER_REGISTRY}/mod-md-debian-sid:0.0.1
6 | container_name: mod-md-debian-sid
7 | build:
8 | context: .
9 | dockerfile: docker/debian-sid/Dockerfile
10 | labels:
11 | - "description=mod_md debian sid server"
12 | - "maintainer=stefan@eissing.org"
13 | ports:
14 | - 14000:14000 # HTTPS ACME API
15 | - 15000:15000 # HTTPS Management API
16 |
17 | volumes:
18 | - mod-md-debian-sid-data:/apache-httpd/data
19 |
20 | volumes:
21 | mod-md-debian-sid-data:
22 | name: mod-md-debian-sid-data
23 | labels:
24 | - "description=debian sid data for mod_md"
25 | - "maintainer=stefan@eissing.org"
26 |
27 |
--------------------------------------------------------------------------------
/docker/debian-sid/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM debian:sid
2 |
3 | RUN apt update; apt upgrade -y
4 | RUN apt install -y apt-listchanges \
5 | make openssl libssl-dev libcurl4 libcurl4-openssl-dev \
6 | gcc subversion git cargo python3 iputils-ping \
7 | libapr1-dev libaprutil1-dev libnghttp2-dev pip \
8 | autoconf libtool libtool-bin libpcre3-dev libjansson-dev curl rsync nghttp2-client
9 |
10 | RUN pip install pytest tqdm pycurl cryptography requests pyopenssl
11 |
12 | RUN apt install -y apache2 apache2-dev libapache2-mod-md
13 | RUN apt install -y pebble
14 |
15 | COPY docker/debian-sid/bin/* /apache-httpd/bin/
16 | COPY configure.ac Makefile.am NEWS README* AUTHORS ChangeLog COPYING LICENSE /apache-httpd/mod_md/
17 | COPY m4 /apache-httpd/mod_md/m4
18 | COPY src /apache-httpd/mod_md/src
19 | COPY test test/Makefile.am /apache-httpd/mod_md/test/
20 | COPY test/pyhttpd /apache-httpd/mod_md/test/pyhttpd
21 | COPY test/modules /apache-httpd/mod_md/test/modules
22 | COPY test/unit /apache-httpd/mod_md/test/unit
23 |
24 | CMD ["/bin/bash", "-c", "/apache-httpd/bin/mod_md_test.sh"]
--------------------------------------------------------------------------------
/docker/debian-sid/bin/mod_md_test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | TOP=/apache-httpd
4 | DATADIR=$TOP/data
5 |
6 | fail() {
7 | echo "$@"
8 | exit 1
9 | }
10 |
11 | needs_update() {
12 | local ref_file="$1"
13 | local check_dir="$2"
14 | if test ! -f "$ref_file"; then
15 | return 0
16 | fi
17 | find "$check_dir" -type f -a -newer "$ref_file" -o -type d -name .git -prune -a -false |
18 | while read fname; do
19 | return 0
20 | done
21 | return 1
22 | }
23 |
24 | PREFIX=$(apxs -q exec_prefix)
25 | if test ! -d $PREFIX; then
26 | fail "apache install prefix not found: $PREFIX"
27 | fi
28 |
29 | # remove some stuff that accumulates
30 | LOG_DIR=$(apxs -q logfiledir)
31 | rm -f $LOG_DIR/*
32 |
33 | cd "$TOP/mod_md" ||fail
34 | if needs_update .installed .; then
35 | rm -f .installed
36 | if test ! -f configure -o configure.ac -nt configure; then
37 | autoreconf -i ||fail
38 | fi
39 | if test ! -d Makefile -o ./configure -nt Makefile; then
40 | ./configure || fail
41 | touch ./configure
42 | fi
43 | make clean||fail
44 | make ||fail
45 | find .
46 | touch .installed
47 | fi
48 | make install ||fail
49 | #pytest -vvv -k test_300_021 || cat test/gen/apache/logs/error_log
50 | pytest
51 |
--------------------------------------------------------------------------------
/m4/ax_check_compile_flag.m4:
--------------------------------------------------------------------------------
1 | # ===========================================================================
2 | # http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
3 | # ===========================================================================
4 | #
5 | # SYNOPSIS
6 | #
7 | # AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
8 | #
9 | # DESCRIPTION
10 | #
11 | # Check whether the given FLAG works with the current language's compiler
12 | # or gives an error. (Warnings, however, are ignored)
13 | #
14 | # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
15 | # success/failure.
16 | #
17 | # If EXTRA-FLAGS is defined, it is added to the current language's default
18 | # flags (e.g. CFLAGS) when the check is done. The check is thus made with
19 | # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
20 | # force the compiler to issue an error when a bad flag is given.
21 | #
22 | # INPUT gives an alternative input source to AC_COMPILE_IFELSE.
23 | #
24 | # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
25 | # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
26 | #
27 | # LICENSE
28 | #
29 | # Copyright (c) 2008 Guido U. Draheim
30 | # Copyright (c) 2011 Maarten Bosmans
31 | #
32 | # This program is free software: you can redistribute it and/or modify it
33 | # under the terms of the GNU General Public License as published by the
34 | # Free Software Foundation, either version 3 of the License, or (at your
35 | # option) any later version.
36 | #
37 | # This program is distributed in the hope that it will be useful, but
38 | # WITHOUT ANY WARRANTY; without even the implied warranty of
39 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
40 | # Public License for more details.
41 | #
42 | # You should have received a copy of the GNU General Public License along
43 | # with this program. If not, see .
44 | #
45 | # As a special exception, the respective Autoconf Macro's copyright owner
46 | # gives unlimited permission to copy, distribute and modify the configure
47 | # scripts that are the output of Autoconf when processing the Macro. You
48 | # need not follow the terms of the GNU General Public License when using
49 | # or distributing such scripts, even though portions of the text of the
50 | # Macro appear in them. The GNU General Public License (GPL) does govern
51 | # all other use of the material that constitutes the Autoconf Macro.
52 | #
53 | # This special exception to the GPL applies to versions of the Autoconf
54 | # Macro released by the Autoconf Archive. When you make and distribute a
55 | # modified version of the Autoconf Macro, you may extend this special
56 | # exception to the GPL to apply to your modified version as well.
57 |
58 | #serial 3
59 |
60 | AC_DEFUN([AX_CHECK_COMPILE_FLAG],
61 | [AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX
62 | AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
63 | AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
64 | ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
65 | _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
66 | AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
67 | [AS_VAR_SET(CACHEVAR,[yes])],
68 | [AS_VAR_SET(CACHEVAR,[no])])
69 | _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
70 | AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes],
71 | [m4_default([$2], :)],
72 | [m4_default([$3], :)])
73 | AS_VAR_POPDEF([CACHEVAR])dnl
74 | ])dnl AX_CHECK_COMPILE_FLAGS
75 |
--------------------------------------------------------------------------------
/mod_md_lib/main.c:
--------------------------------------------------------------------------------
1 | //
2 | // main.c
3 | // mod_md_lib
4 | //
5 | // Created by Stefan Eissing on 29.05.19.
6 | //
7 |
8 | #include
9 |
10 | int main(int argc, const char * argv[]) {
11 | // insert code here...
12 | printf("Hello, World!\n");
13 | return 0;
14 | }
15 |
--------------------------------------------------------------------------------
/mod_md_ocsp_status.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/mod_md_ocsp_status.png
--------------------------------------------------------------------------------
/mod_md_status.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/mod_md_status.png
--------------------------------------------------------------------------------
/patches/mod_ssl_md2-2.4.x.diff:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/patches/mod_ssl_md2-trunk.diff:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/scripts/contributors.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #***************************************************************************
3 | # _ _ ____ _
4 | # Project ___| | | | _ \| |
5 | # / __| | | | |_) | |
6 | # | (__| |_| | _ <| |___
7 | # \___|\___/|_| \_\_____|
8 | #
9 | # Copyright (C) 2013-2018, Daniel Stenberg, , et al.
10 | #
11 | # This software is licensed as described in the file COPYING, which
12 | # you should have received as part of this distribution. The terms
13 | # are also available at https://curl.haxx.se/docs/copyright.html.
14 | #
15 | # You may opt to use, copy, modify, merge, publish, distribute and/or sell
16 | # copies of the Software, and permit persons to whom the Software is
17 | # furnished to do so, under the terms of the COPYING file.
18 | #
19 | # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 | # KIND, either express or implied.
21 | #
22 | ###########################################################################
23 | #
24 | # This script was inspired, partly copied and modified from the great curl project.
25 | # see
26 | #
27 | #
28 |
29 | rcommit=""
30 |
31 | if test $# -le 0; then
32 | rtag=$( git tag --sort=authordate | tail -n 1 )
33 | else
34 | case "$1" in
35 | v*)
36 | rtag="$1"
37 | ;;
38 | *)
39 | rcommit=$1
40 | ;;
41 | esac
42 | fi
43 |
44 | if test -n "$rtag"; then
45 | git show ${rtag} -w >/dev/null || exit 1
46 | rcommit=$( git show ${rtag} -w |egrep '^commit '|cut -d' ' -f2 )
47 | echo "since-release: ${rtag}"
48 | fi
49 |
50 | echo "since-commit: $rcommit"
51 | (
52 | git log --use-mailmap $rcommit..HEAD | \
53 | egrep -ai '(^Author|^Commit|by):' | \
54 | cut -d: -f2- | \
55 | cut '-d(' -f1 | \
56 | cut '-d<' -f1 | \
57 | tr , '\012' | \
58 | sed 's/ at github/ on github/' | \
59 | sed 's/ and /\n/' | \
60 | sed -e 's/^ //' -e 's/ $//g' -e 's/@users.noreply.github.com$/ on github/'
61 |
62 | )| \
63 | grep -a ' ' | \
64 | sort -fu | \
65 | awk '
66 | BEGIN {
67 | sep = ""
68 | field="contributor-names: "
69 | }
70 |
71 | {
72 | num++;
73 | n = sprintf("%s%s%s", n, sep, $0);
74 | sep = ", "
75 | if(length(n) > 77) {
76 | printf("%s%s%s\n", field, p, sep);
77 | field=" "
78 | n=sprintf("%s", $0);
79 | }
80 | p=n;
81 | }
82 |
83 | END {
84 | printf("%s%s\n", field, p);
85 | printf("contributor-count: %d\n", num);
86 | }
87 | '
88 |
--------------------------------------------------------------------------------
/scripts/md_message.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # Sample script to configure for MDMessageCmd.
4 | #
5 | # Edit the USER you want to notify. Comment the "msg=" lines where
6 | # you do not want to receive notifications for.
7 | #
8 |
9 | action="$1"
10 | domain="$2"
11 |
12 | USER="webmaster@$domain"
13 |
14 | case "$action" in
15 | "renewing")
16 | subject="renewing $domain certificate"
17 | msg="Your Apache starts renewing the certificate for '$domain'."
18 | ;;
19 | "renewed")
20 | subject="renewed $domain certificate"
21 | msg="Your Apache renewed the certificate for '$domain'. It will become active after a server reload."
22 | ;;
23 | "installed")
24 | subject="installed $domain certificate"
25 | msg="Your Apache installed the certificate for '$domain'. It is now active."
26 | ;;
27 | "expiring")
28 | subject="expiring $domain certificate"
29 | msg="Your Apache reports that the certificate for '$domain' will soon expire."
30 | ;;
31 | "errored")
32 | subject="error renewing $domain certificate"
33 | msg="There was an error renewing the certificate for '$domain'. Apache will continue trying. Please check the md-status resources or the server log for more information should this repeat."
34 | ;;
35 | "ocsp-renewed")
36 | subject="refreshed OCSP stapling for $domain"
37 | msg="The OCSP stapling information for '$domain' was successfully refreshed."
38 | ;;
39 | "ocsp-errored")
40 | subject="error refreshing OCSP stapling for $domain"
41 | msg="The was an error refreshing the OCSP stapling information for '$domain'. Apache will continue trying. Please check the md-status resources or the server log for more information should this repeat."
42 | ;;
43 | *)
44 | subject="unknown action in MD message"
45 | msg="Your Apache reported action '$action' for domain '$domain'."
46 | esac
47 |
48 | if test "x$msg" = "x"; then exit 0; fi
49 |
50 | mail -s "$subject" "$USER" <
18 | #include
19 | #include
20 |
21 | #include "md.h"
22 | #include "md_event.h"
23 |
24 |
25 | typedef struct md_subscription {
26 | struct md_subscription *next;
27 | md_event_cb *cb;
28 | void *baton;
29 | } md_subscription;
30 |
31 | static struct {
32 | apr_pool_t *p;
33 | md_subscription *subs;
34 | } EVNT;
35 |
36 | static apr_status_t cleanup_setup(void *dummy)
37 | {
38 | (void)dummy;
39 | memset(&EVNT, 0, sizeof(EVNT));
40 | return APR_SUCCESS;
41 | }
42 |
43 | void md_event_init(apr_pool_t *p)
44 | {
45 | memset(&EVNT, 0, sizeof(EVNT));
46 | EVNT.p = p;
47 | apr_pool_cleanup_register(p, NULL, cleanup_setup, apr_pool_cleanup_null);
48 | }
49 |
50 | void md_event_subscribe(md_event_cb *cb, void *baton)
51 | {
52 | md_subscription *sub;
53 |
54 | sub = apr_pcalloc(EVNT.p, sizeof(*sub));
55 | sub->cb = cb;
56 | sub->baton = baton;
57 | sub->next = EVNT.subs;
58 | EVNT.subs = sub;
59 | }
60 |
61 | apr_status_t md_event_raise(const char *event,
62 | const char *mdomain,
63 | struct md_job_t *job,
64 | struct md_result_t *result,
65 | apr_pool_t *p)
66 | {
67 | md_subscription *sub = EVNT.subs;
68 | apr_status_t rv;
69 |
70 | while (sub) {
71 | rv = sub->cb(event, mdomain, sub->baton, job, result, p);
72 | if (APR_SUCCESS != rv) return rv;
73 | sub = sub->next;
74 | }
75 | return APR_SUCCESS;
76 | }
77 |
78 | void md_event_holler(const char *event,
79 | const char *mdomain,
80 | struct md_job_t *job,
81 | struct md_result_t *result,
82 | apr_pool_t *p)
83 | {
84 | md_subscription *sub = EVNT.subs;
85 | while (sub) {
86 | sub->cb(event, mdomain, sub->baton, job, result, p);
87 | sub = sub->next;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/md_event.h:
--------------------------------------------------------------------------------
1 | /* Licensed to the Apache Software Foundation (ASF) under one or more
2 | * contributor license agreements. See the NOTICE file distributed with
3 | * this work for additional information regarding copyright ownership.
4 | * The ASF licenses this file to You under the Apache License, Version 2.0
5 | * (the "License"); you may not use this file except in compliance with
6 | * the License. You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef md_event_h
18 | #define md_event_h
19 |
20 | struct md_job_t;
21 | struct md_result_t;
22 |
23 | typedef apr_status_t md_event_cb(const char *event,
24 | const char *mdomain,
25 | void *baton,
26 | struct md_job_t *job,
27 | struct md_result_t *result,
28 | apr_pool_t *p);
29 |
30 | void md_event_init(apr_pool_t *p);
31 |
32 | void md_event_subscribe(md_event_cb *cb, void *baton);
33 |
34 | apr_status_t md_event_raise(const char *event,
35 | const char *mdomain,
36 | struct md_job_t *job,
37 | struct md_result_t *result,
38 | apr_pool_t *p);
39 |
40 | void md_event_holler(const char *event,
41 | const char *mdomain,
42 | struct md_job_t *job,
43 | struct md_result_t *result,
44 | apr_pool_t *p);
45 |
46 | #endif /* md_event_h */
47 |
--------------------------------------------------------------------------------
/src/md_jws.h:
--------------------------------------------------------------------------------
1 | /* Licensed to the Apache Software Foundation (ASF) under one or more
2 | * contributor license agreements. See the NOTICE file distributed with
3 | * this work for additional information regarding copyright ownership.
4 | * The ASF licenses this file to You under the Apache License, Version 2.0
5 | * (the "License"); you may not use this file except in compliance with
6 | * the License. You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef mod_md_md_jws_h
18 | #define mod_md_md_jws_h
19 |
20 | struct apr_table_t;
21 | struct md_json_t;
22 | struct md_pkey_t;
23 | struct md_data_t;
24 |
25 | /**
26 | * Get the JSON value of the 'jwk' field for the given key.
27 | */
28 | apr_status_t md_jws_get_jwk(md_json_t **pjwk, apr_pool_t *p, struct md_pkey_t *pkey);
29 |
30 | /**
31 | * Get the JWS key signed JSON message with given payload and protected fields, signed
32 | * using the given key and optional key_id.
33 | */
34 | apr_status_t md_jws_sign(md_json_t **pmsg, apr_pool_t *p,
35 | struct md_data_t *payload, md_json_t *prot_fields,
36 | struct md_pkey_t *pkey, const char *key_id);
37 | /**
38 | * Get the 'Thumbprint' as defined in RFC8555 for the given key in
39 | * base64 encoding.
40 | */
41 | apr_status_t md_jws_pkey_thumb(const char **pthumb64, apr_pool_t *p, struct md_pkey_t *pkey);
42 |
43 | /**
44 | * Get the JWS HS256 signed message for given payload and protected fields,
45 | * using the base64 encoded MAC key.
46 | */
47 | apr_status_t md_jws_hmac(md_json_t **pmsg, apr_pool_t *p,
48 | struct md_data_t *payload, md_json_t *prot_fields,
49 | const struct md_data_t *hmac_key);
50 |
51 |
52 | #endif /* md_jws_h */
53 |
--------------------------------------------------------------------------------
/src/md_log.c:
--------------------------------------------------------------------------------
1 | /* Licensed to the Apache Software Foundation (ASF) under one or more
2 | * contributor license agreements. See the NOTICE file distributed with
3 | * this work for additional information regarding copyright ownership.
4 | * The ASF licenses this file to You under the Apache License, Version 2.0
5 | * (the "License"); you may not use this file except in compliance with
6 | * the License. You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include
18 | #include
19 | #include
20 |
21 | #include "md_log.h"
22 |
23 | #define LOG_BUFFER_LEN 1024
24 |
25 | static const char *level_names[] = {
26 | "emergency",
27 | "alert",
28 | "crit",
29 | "err",
30 | "warning",
31 | "notice",
32 | "info",
33 | "debug",
34 | "trace1",
35 | "trace2",
36 | "trace3",
37 | "trace4",
38 | "trace5",
39 | "trace6",
40 | "trace7",
41 | "trace8",
42 | };
43 |
44 | const char *md_log_level_name(md_log_level_t level)
45 | {
46 | return level_names[level];
47 | }
48 |
49 | static md_log_print_cb *log_printv;
50 | static md_log_level_cb *log_level;
51 | static void *log_baton;
52 |
53 | void md_log_set(md_log_level_cb *level_cb, md_log_print_cb *print_cb, void *baton)
54 | {
55 | log_printv = print_cb;
56 | log_level = level_cb;
57 | log_baton = baton;
58 | }
59 |
60 | int md_log_is_level(apr_pool_t *p, md_log_level_t level)
61 | {
62 | if (!log_level) {
63 | return 0;
64 | }
65 | return log_level(log_baton, p, level);
66 | }
67 |
68 | void md_log_perror(const char *file, int line, md_log_level_t level,
69 | apr_status_t rv, apr_pool_t *p, const char *fmt, ...)
70 | {
71 | va_list ap;
72 |
73 | va_start(ap, fmt);
74 | if (log_printv) {
75 | log_printv(file, line, level, rv, log_baton, p, fmt, ap);
76 | }
77 | va_end(ap);
78 | }
79 |
--------------------------------------------------------------------------------
/src/md_log.h:
--------------------------------------------------------------------------------
1 | /* Licensed to the Apache Software Foundation (ASF) under one or more
2 | * contributor license agreements. See the NOTICE file distributed with
3 | * this work for additional information regarding copyright ownership.
4 | * The ASF licenses this file to You under the Apache License, Version 2.0
5 | * (the "License"); you may not use this file except in compliance with
6 | * the License. You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef mod_md_md_log_h
18 | #define mod_md_md_log_h
19 |
20 | typedef enum {
21 | MD_LOG_EMERG,
22 | MD_LOG_ALERT,
23 | MD_LOG_CRIT,
24 | MD_LOG_ERR,
25 | MD_LOG_WARNING,
26 | MD_LOG_NOTICE,
27 | MD_LOG_INFO,
28 | MD_LOG_DEBUG,
29 | MD_LOG_TRACE1,
30 | MD_LOG_TRACE2,
31 | MD_LOG_TRACE3,
32 | MD_LOG_TRACE4,
33 | MD_LOG_TRACE5,
34 | MD_LOG_TRACE6,
35 | MD_LOG_TRACE7,
36 | MD_LOG_TRACE8,
37 | } md_log_level_t;
38 |
39 | #define MD_LOG_MARK __FILE__,__LINE__
40 |
41 | #ifndef APLOGNO
42 | #define APLOGNO(n) "AH" #n ": "
43 | #endif
44 |
45 | const char *md_log_level_name(md_log_level_t level);
46 |
47 | int md_log_is_level(apr_pool_t *p, md_log_level_t level);
48 |
49 | void md_log_perror(const char *file, int line, md_log_level_t level,
50 | apr_status_t rv, apr_pool_t *p, const char *fmt, ...)
51 | __attribute__((format(printf,6,7)));
52 |
53 | typedef int md_log_level_cb(void *baton, apr_pool_t *p, md_log_level_t level);
54 |
55 | typedef void md_log_print_cb(const char *file, int line, md_log_level_t level,
56 | apr_status_t rv, void *baton, apr_pool_t *p, const char *fmt, va_list ap);
57 |
58 | void md_log_set(md_log_level_cb *level_cb, md_log_print_cb *print_cb, void *baton);
59 |
60 | #endif /* md_log_h */
61 |
--------------------------------------------------------------------------------
/src/md_ocsp.h:
--------------------------------------------------------------------------------
1 | /* Licensed to the Apache Software Foundation (ASF) under one or more
2 | * contributor license agreements. See the NOTICE file distributed with
3 | * this work for additional information regarding copyright ownership.
4 | * The ASF licenses this file to You under the Apache License, Version 2.0
5 | * (the "License"); you may not use this file except in compliance with
6 | * the License. You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef md_ocsp_h
18 | #define md_ocsp_h
19 |
20 | struct md_data_t;
21 | struct md_job_t;
22 | struct md_json_t;
23 | struct md_result_t;
24 | struct md_store_t;
25 | struct md_timeslice_t;
26 |
27 | typedef enum {
28 | MD_OCSP_CERT_ST_UNKNOWN,
29 | MD_OCSP_CERT_ST_GOOD,
30 | MD_OCSP_CERT_ST_REVOKED,
31 | } md_ocsp_cert_stat_t;
32 |
33 | const char *md_ocsp_cert_stat_name(md_ocsp_cert_stat_t stat);
34 | md_ocsp_cert_stat_t md_ocsp_cert_stat_value(const char *name);
35 |
36 | typedef struct md_ocsp_reg_t md_ocsp_reg_t;
37 |
38 | apr_status_t md_ocsp_reg_make(md_ocsp_reg_t **preg, apr_pool_t *p,
39 | struct md_store_t *store,
40 | const md_timeslice_t *renew_window,
41 | const char *user_agent, const char *proxy_url,
42 | apr_time_t min_delay);
43 |
44 | apr_status_t md_ocsp_init_id(struct md_data_t *id, apr_pool_t *p, const md_cert_t *cert);
45 |
46 | apr_status_t md_ocsp_prime(md_ocsp_reg_t *reg, const char *ext_id, apr_size_t ext_id_len,
47 | md_cert_t *x, md_cert_t *issuer, const md_t *md);
48 |
49 | typedef void md_ocsp_copy_der(const unsigned char *der, apr_size_t der_len, void *userdata);
50 |
51 | apr_status_t md_ocsp_get_status(md_ocsp_copy_der *cb, void *userdata, md_ocsp_reg_t *reg,
52 | const char *ext_id, apr_size_t ext_id_len,
53 | apr_pool_t *p, const md_t *md);
54 |
55 | apr_status_t md_ocsp_get_meta(md_ocsp_cert_stat_t *pstat, md_timeperiod_t *pvalid,
56 | md_ocsp_reg_t *reg, const md_cert_t *cert,
57 | apr_pool_t *p, const md_t *md);
58 |
59 | apr_size_t md_ocsp_count(md_ocsp_reg_t *reg);
60 |
61 | void md_ocsp_renew(md_ocsp_reg_t *reg, apr_pool_t *p, apr_pool_t *ptemp, apr_time_t *pnext_run);
62 |
63 | apr_status_t md_ocsp_remove_responses_older_than(md_ocsp_reg_t *reg, apr_pool_t *p,
64 | apr_time_t timestamp);
65 |
66 | void md_ocsp_get_summary(struct md_json_t **pjson, md_ocsp_reg_t *reg, apr_pool_t *p);
67 | void md_ocsp_get_status_all(struct md_json_t **pjson, md_ocsp_reg_t *reg, apr_pool_t *p);
68 |
69 | struct md_job_t *md_ocsp_job_make(md_ocsp_reg_t *ocsp, const char *mdomain, apr_pool_t *p);
70 |
71 | #endif /* md_ocsp_h */
72 |
--------------------------------------------------------------------------------
/src/md_store_fs.h:
--------------------------------------------------------------------------------
1 | /* Licensed to the Apache Software Foundation (ASF) under one or more
2 | * contributor license agreements. See the NOTICE file distributed with
3 | * this work for additional information regarding copyright ownership.
4 | * The ASF licenses this file to You under the Apache License, Version 2.0
5 | * (the "License"); you may not use this file except in compliance with
6 | * the License. You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef mod_md_md_store_fs_h
18 | #define mod_md_md_store_fs_h
19 |
20 | struct md_store_t;
21 |
22 | /**
23 | * Default file permissions set by the store, user only read/write(/exec),
24 | * if so supported by the apr.
25 | */
26 | #define MD_FPROT_F_UONLY (APR_FPROT_UREAD|APR_FPROT_UWRITE)
27 | #define MD_FPROT_D_UONLY (MD_FPROT_F_UONLY|APR_FPROT_UEXECUTE)
28 |
29 | /**
30 | * User has all permission, group can read, other none
31 | */
32 | #define MD_FPROT_F_UALL_GREAD (MD_FPROT_F_UONLY|APR_FPROT_GREAD)
33 | #define MD_FPROT_D_UALL_GREAD (MD_FPROT_D_UONLY|APR_FPROT_GREAD|APR_FPROT_GEXECUTE)
34 |
35 | /**
36 | * User has all permission, group and others can read
37 | */
38 | #define MD_FPROT_F_UALL_WREAD (MD_FPROT_F_UALL_GREAD|APR_FPROT_WREAD)
39 | #define MD_FPROT_D_UALL_WREAD (MD_FPROT_D_UALL_GREAD|APR_FPROT_WREAD|APR_FPROT_WEXECUTE)
40 |
41 | apr_status_t md_store_fs_init(struct md_store_t **pstore, apr_pool_t *p,
42 | const char *path);
43 |
44 |
45 | apr_status_t md_store_fs_default_perms_set(struct md_store_t *store,
46 | apr_fileperms_t file_perms,
47 | apr_fileperms_t dir_perms);
48 | apr_status_t md_store_fs_group_perms_set(struct md_store_t *store,
49 | md_store_group_t group,
50 | apr_fileperms_t file_perms,
51 | apr_fileperms_t dir_perms);
52 |
53 | typedef enum {
54 | MD_S_FS_EV_CREATED,
55 | MD_S_FS_EV_MOVED,
56 | } md_store_fs_ev_t;
57 |
58 | typedef apr_status_t md_store_fs_cb(void *baton, struct md_store_t *store,
59 | md_store_fs_ev_t ev, unsigned int group,
60 | const char *fname, apr_filetype_e ftype,
61 | apr_pool_t *p);
62 |
63 | apr_status_t md_store_fs_set_event_cb(struct md_store_t *store, md_store_fs_cb *cb, void *baton);
64 |
65 | #endif /* mod_md_md_store_fs_h */
66 |
--------------------------------------------------------------------------------
/src/md_tailscale.h:
--------------------------------------------------------------------------------
1 | /* Licensed to the Apache Software Foundation (ASF) under one or more
2 | * contributor license agreements. See the NOTICE file distributed with
3 | * this work for additional information regarding copyright ownership.
4 | * The ASF licenses this file to You under the Apache License, Version 2.0
5 | * (the "License"); you may not use this file except in compliance with
6 | * the License. You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef mod_md_md_tailscale_h
18 | #define mod_md_md_tailscale_h
19 |
20 | #define MD_PROTO_TAILSCALE "tailscale"
21 |
22 | apr_status_t md_tailscale_protos_add(struct apr_hash_t *protos, apr_pool_t *p);
23 |
24 | #endif /* mod_md_md_tailscale_h */
25 |
26 |
--------------------------------------------------------------------------------
/src/md_time.h:
--------------------------------------------------------------------------------
1 | /* Licensed to the Apache Software Foundation (ASF) under one or more
2 | * contributor license agreements. See the NOTICE file distributed with
3 | * this work for additional information regarding copyright ownership.
4 | * The ASF licenses this file to You under the Apache License, Version 2.0
5 | * (the "License"); you may not use this file except in compliance with
6 | * the License. You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef mod_md_md_time_h
18 | #define mod_md_md_time_h
19 |
20 | #include
21 |
22 | #define MD_SECS_PER_HOUR (60*60)
23 | #define MD_SECS_PER_DAY (24*MD_SECS_PER_HOUR)
24 |
25 | typedef struct md_timeperiod_t md_timeperiod_t;
26 |
27 | struct md_timeperiod_t {
28 | apr_time_t start;
29 | apr_time_t end;
30 | };
31 |
32 | apr_time_t md_timeperiod_length(const md_timeperiod_t *period);
33 |
34 | int md_timeperiod_contains(const md_timeperiod_t *period, apr_time_t time);
35 | int md_timeperiod_has_started(const md_timeperiod_t *period, apr_time_t time);
36 | int md_timeperiod_has_ended(const md_timeperiod_t *period, apr_time_t time);
37 | apr_interval_time_t md_timeperiod_remaining(const md_timeperiod_t *period, apr_time_t time);
38 |
39 | /**
40 | * Return the timeperiod common between a and b. If both do not overlap, return {0,0}.
41 | */
42 | md_timeperiod_t md_timeperiod_common(const md_timeperiod_t *a, const md_timeperiod_t *b);
43 |
44 | char *md_timeperiod_print(apr_pool_t *p, const md_timeperiod_t *period);
45 |
46 | /**
47 | * Print a human readable form of the give duration in days/hours/min/sec
48 | */
49 | const char *md_duration_print(apr_pool_t *p, apr_interval_time_t duration);
50 | const char *md_duration_roughly(apr_pool_t *p, apr_interval_time_t duration);
51 |
52 | /**
53 | * Parse a machine readable string duration in the form of NN[unit], where
54 | * unit is d/h/mi/s/ms with the default given should the unit not be specified.
55 | */
56 | apr_status_t md_duration_parse(apr_interval_time_t *ptimeout, const char *value,
57 | const char *def_unit);
58 | const char *md_duration_format(apr_pool_t *p, apr_interval_time_t duration);
59 |
60 | typedef struct {
61 | apr_interval_time_t norm; /* if > 0, normalized base length */
62 | apr_interval_time_t len; /* length of the timespan */
63 | } md_timeslice_t;
64 |
65 | apr_status_t md_timeslice_create(md_timeslice_t **pts, apr_pool_t *p,
66 | apr_interval_time_t norm, apr_interval_time_t len);
67 |
68 | int md_timeslice_eq(const md_timeslice_t *ts1, const md_timeslice_t *ts2);
69 |
70 | const char *md_timeslice_parse(md_timeslice_t **pts, apr_pool_t *p,
71 | const char *val, apr_interval_time_t defnorm);
72 | const char *md_timeslice_format(const md_timeslice_t *ts, apr_pool_t *p);
73 |
74 | md_timeperiod_t md_timeperiod_slice_before_end(const md_timeperiod_t *period,
75 | const md_timeslice_t *ts);
76 |
77 | #endif /* md_util_h */
78 |
--------------------------------------------------------------------------------
/src/md_version.h:
--------------------------------------------------------------------------------
1 | /* Licensed to the Apache Software Foundation (ASF) under one or more
2 | * contributor license agreements. See the NOTICE file distributed with
3 | * this work for additional information regarding copyright ownership.
4 | * The ASF licenses this file to You under the Apache License, Version 2.0
5 | * (the "License"); you may not use this file except in compliance with
6 | * the License. You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef mod_md_md_version_h
18 | #define mod_md_md_version_h
19 |
20 | #undef PACKAGE_VERSION
21 | #undef PACKAGE_TARNAME
22 | #undef PACKAGE_STRING
23 | #undef PACKAGE_NAME
24 | #undef PACKAGE_BUGREPORT
25 |
26 | /**
27 | * @macro
28 | * Version number of the md module as c string
29 | */
30 | #define MOD_MD_VERSION "2.5.2-git"
31 |
32 | /**
33 | * @macro
34 | * Numerical representation of the version number of the md module
35 | * release. This is a 24 bit number with 8 bits for major number, 8 bits
36 | * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
37 | */
38 | #define MOD_MD_VERSION_NUM 0x020502
39 |
40 | #define MD_ACME_DEF_URL "https://acme-v02.api.letsencrypt.org/directory"
41 | #define MD_TAILSCALE_DEF_URL "file://localhost/var/run/tailscale/tailscaled.sock"
42 |
43 | #endif /* mod_md_md_version_h */
44 |
--------------------------------------------------------------------------------
/src/md_version.h.in:
--------------------------------------------------------------------------------
1 | /* Licensed to the Apache Software Foundation (ASF) under one or more
2 | * contributor license agreements. See the NOTICE file distributed with
3 | * this work for additional information regarding copyright ownership.
4 | * The ASF licenses this file to You under the Apache License, Version 2.0
5 | * (the "License"); you may not use this file except in compliance with
6 | * the License. You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef mod_md_md_version_h
18 | #define mod_md_md_version_h
19 |
20 | #undef PACKAGE_VERSION
21 | #undef PACKAGE_TARNAME
22 | #undef PACKAGE_STRING
23 | #undef PACKAGE_NAME
24 | #undef PACKAGE_BUGREPORT
25 |
26 | /**
27 | * @macro
28 | * Version number of the md module as c string
29 | */
30 | #define MOD_MD_VERSION "@PACKAGE_VERSION@-git"
31 |
32 | /**
33 | * @macro
34 | * Numerical representation of the version number of the md module
35 | * release. This is a 24 bit number with 8 bits for major number, 8 bits
36 | * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
37 | */
38 | #define MOD_MD_VERSION_NUM @PACKAGE_VERSION_NUM@
39 |
40 | #define MD_ACME_DEF_URL "@ACME_DEF_URL@"
41 | #define MD_TAILSCALE_DEF_URL "@TAILSCALE_DEF_URL@"
42 |
43 | #endif /* mod_md_md_version_h */
44 |
--------------------------------------------------------------------------------
/src/mod_md.h:
--------------------------------------------------------------------------------
1 | /* Licensed to the Apache Software Foundation (ASF) under one or more
2 | * contributor license agreements. See the NOTICE file distributed with
3 | * this work for additional information regarding copyright ownership.
4 | * The ASF licenses this file to You under the Apache License, Version 2.0
5 | * (the "License"); you may not use this file except in compliance with
6 | * the License. You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef mod_md_mod_md_h
18 | #define mod_md_mod_md_h
19 |
20 | #endif /* mod_md_mod_md_h */
21 |
--------------------------------------------------------------------------------
/src/mod_md_drive.h:
--------------------------------------------------------------------------------
1 | /* Licensed to the Apache Software Foundation (ASF) under one or more
2 | * contributor license agreements. See the NOTICE file distributed with
3 | * this work for additional information regarding copyright ownership.
4 | * The ASF licenses this file to You under the Apache License, Version 2.0
5 | * (the "License"); you may not use this file except in compliance with
6 | * the License. You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef mod_md_md_drive_h
18 | #define mod_md_md_drive_h
19 |
20 | struct md_mod_conf_t;
21 | struct md_reg_t;
22 |
23 | typedef struct md_renew_ctx_t md_renew_ctx_t;
24 |
25 | int md_will_renew_cert(const md_t *md);
26 |
27 | /**
28 | * Start driving the certificate renewal for MDs marked with watched.
29 | */
30 | apr_status_t md_renew_start_watching(struct md_mod_conf_t *mc, server_rec *s, apr_pool_t *p);
31 |
32 |
33 |
34 |
35 | #endif /* mod_md_md_drive_h */
36 |
--------------------------------------------------------------------------------
/src/mod_md_ocsp.h:
--------------------------------------------------------------------------------
1 | /* Licensed to the Apache Software Foundation (ASF) under one or more
2 | * contributor license agreements. See the NOTICE file distributed with
3 | * this work for additional information regarding copyright ownership.
4 | * The ASF licenses this file to You under the Apache License, Version 2.0
5 | * (the "License"); you may not use this file except in compliance with
6 | * the License. You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef mod_md_md_ocsp_h
18 | #define mod_md_md_ocsp_h
19 |
20 |
21 | int md_ocsp_prime_status(server_rec *s, apr_pool_t *p,
22 | const char *id, apr_size_t id_len, const char *pem);
23 |
24 | int md_ocsp_provide_status(server_rec *s, conn_rec *c, const char *id, apr_size_t id_len,
25 | ap_ssl_ocsp_copy_resp *cb, void *userdata);
26 |
27 | /**
28 | * Start watchdog for retrieving/updating ocsp status.
29 | */
30 | apr_status_t md_ocsp_start_watching(struct md_mod_conf_t *mc, server_rec *s, apr_pool_t *p);
31 |
32 |
33 | #endif /* mod_md_md_ocsp_h */
34 |
--------------------------------------------------------------------------------
/src/mod_md_os.c:
--------------------------------------------------------------------------------
1 | /* Licensed to the Apache Software Foundation (ASF) under one or more
2 | * contributor license agreements. See the NOTICE file distributed with
3 | * this work for additional information regarding copyright ownership.
4 | * The ASF licenses this file to You under the Apache License, Version 2.0
5 | * (the "License"); you may not use this file except in compliance with
6 | * the License. You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include
18 | #include
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 |
25 | #if APR_HAVE_UNISTD_H
26 | #include
27 | #endif
28 | #if AP_NEED_SET_MUTEX_PERMS
29 | #include "unixd.h"
30 | #endif
31 |
32 | #include "md_util.h"
33 | #include "mod_md_os.h"
34 |
35 | apr_status_t md_try_chown(const char *fname, unsigned int uid, int gid, apr_pool_t *p)
36 | {
37 | #if AP_NEED_SET_MUTEX_PERMS && HAVE_UNISTD_H
38 | /* Since we only switch user when running as root, we only need to chown directories
39 | * in that case. Otherwise, the server will ignore any "user/group" directives and
40 | * child processes have the same privileges as the parent.
41 | */
42 | if (!geteuid()) {
43 | if (-1 == chown(fname, (uid_t)uid, (gid_t)gid)) {
44 | apr_status_t rv = APR_FROM_OS_ERROR(errno);
45 | if (!APR_STATUS_IS_ENOENT(rv)) {
46 | ap_log_perror(APLOG_MARK, APLOG_ERR, rv, p, APLOGNO(10082)
47 | "Can't change owner of %s", fname);
48 | }
49 | return rv;
50 | }
51 | }
52 | return APR_SUCCESS;
53 | #else
54 | return APR_ENOTIMPL;
55 | #endif
56 | }
57 |
58 | apr_status_t md_make_worker_accessible(const char *fname, apr_pool_t *p)
59 | {
60 | #ifdef WIN32
61 | return APR_ENOTIMPL;
62 | #else
63 | return md_try_chown(fname, ap_unixd_config.user_id, -1, p);
64 | #endif
65 | }
66 |
67 | #ifdef WIN32
68 |
69 | apr_status_t md_server_graceful(apr_pool_t *p, server_rec *s)
70 | {
71 | return APR_ENOTIMPL;
72 | }
73 |
74 | #else
75 |
76 | apr_status_t md_server_graceful(apr_pool_t *p, server_rec *s)
77 | {
78 | apr_status_t rv;
79 |
80 | (void)p;
81 | (void)s;
82 | rv = (kill(getppid(), AP_SIG_GRACEFUL) < 0)? APR_ENOTIMPL : APR_SUCCESS;
83 | ap_log_error(APLOG_MARK, APLOG_TRACE1, errno, NULL, "sent signal to parent");
84 | return rv;
85 | }
86 |
87 | #endif
88 |
89 |
--------------------------------------------------------------------------------
/src/mod_md_os.h:
--------------------------------------------------------------------------------
1 | /* Licensed to the Apache Software Foundation (ASF) under one or more
2 | * contributor license agreements. See the NOTICE file distributed with
3 | * this work for additional information regarding copyright ownership.
4 | * The ASF licenses this file to You under the Apache License, Version 2.0
5 | * (the "License"); you may not use this file except in compliance with
6 | * the License. You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef mod_md_md_os_h
18 | #define mod_md_md_os_h
19 |
20 | /**
21 | * Try chown'ing the file/directory. Give id -1 to not change uid/gid.
22 | * Will return APR_ENOTIMPL on platforms not supporting this operation.
23 | */
24 | apr_status_t md_try_chown(const char *fname, unsigned int uid, int gid, apr_pool_t *p);
25 |
26 | /**
27 | * Make a file or directory read/write(/searchable) by httpd workers.
28 | */
29 | apr_status_t md_make_worker_accessible(const char *fname, apr_pool_t *p);
30 |
31 | /**
32 | * Trigger a graceful restart of the server. Depending on the architecture, may
33 | * return APR_ENOTIMPL.
34 | */
35 | apr_status_t md_server_graceful(apr_pool_t *p, server_rec *s);
36 |
37 | #endif /* mod_md_md_os_h */
38 |
--------------------------------------------------------------------------------
/src/mod_md_private.h:
--------------------------------------------------------------------------------
1 | /* Licensed to the Apache Software Foundation (ASF) under one or more
2 | * contributor license agreements. See the NOTICE file distributed with
3 | * this work for additional information regarding copyright ownership.
4 | * The ASF licenses this file to You under the Apache License, Version 2.0
5 | * (the "License"); you may not use this file except in compliance with
6 | * the License. You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef mod_md_md_private_h
18 | #define mod_md_md_private_h
19 |
20 | extern module AP_MODULE_DECLARE_DATA md_module;
21 |
22 | APLOG_USE_MODULE(md);
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/src/mod_md_status.h:
--------------------------------------------------------------------------------
1 | /* Licensed to the Apache Software Foundation (ASF) under one or more
2 | * contributor license agreements. See the NOTICE file distributed with
3 | * this work for additional information regarding copyright ownership.
4 | * The ASF licenses this file to You under the Apache License, Version 2.0
5 | * (the "License"); you may not use this file except in compliance with
6 | * the License. You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef mod_md_md_status_h
18 | #define mod_md_md_status_h
19 |
20 | int md_http_cert_status(request_rec *r);
21 |
22 | int md_domains_status_hook(request_rec *r, int flags);
23 | int md_ocsp_status_hook(request_rec *r, int flags);
24 |
25 | int md_status_handler(request_rec *r);
26 |
27 | #endif /* mod_md_md_status_h */
28 |
--------------------------------------------------------------------------------
/test/.gitignore:
--------------------------------------------------------------------------------
1 | Makefile.in
2 | Makefile
3 | __pycache__
4 | .test_dir
5 | gen
6 | conf/global.conf
7 | conf/httpd_http.conf
8 | conf/httpd_https.conf
9 | conf/modules.conf
10 | data/test_conf_validate/test_014.conf
11 | *.pyc
12 | data/test_roundtrip/temp.conf
13 | config.ini
--------------------------------------------------------------------------------
/test/Makefile.am:
--------------------------------------------------------------------------------
1 | # Copyright 2019 greenbytes GmbH (https://www.greenbytes.de)
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 |
16 | SERVER_DIR = @SERVER_DIR@
17 | GEN = gen
18 | ACME_TEST_URL = @ACME_TEST_URL@
19 | ACME_TEST_DIR = @ACME_TEST_DIR@
20 |
21 |
22 | .phony: unit_tests
23 |
24 | EXTRA_DIST = modules pyhttpd unit
25 |
26 | dist-hook:
27 | rm -rf $(distdir)/pyhttpd/htdocs/test1/apache.org-files
28 | rm -rf $(distdir)/pyhttpd/__pycache__
29 | rm -rf $(distdir)/modules/*/__pycache__
30 |
31 |
32 | if BUILD_UNIT_TESTS
33 | TESTS = unit/main
34 |
35 | check_PROGRAMS = unit/main
36 |
37 | unit_main_SOURCES = unit/main.c unit/test_md_json.c unit/test_md_util.c unit/test_common.h
38 | unit_main_LDADD = $(top_builddir)/src/libmd.la
39 |
40 | unit_main_CFLAGS = $(CHECK_CFLAGS) -I$(top_srcdir)/src
41 | unit_main_LDADD += $(CHECK_LIBS) -l$(LIB_APR) -l$(LIB_APRUTIL)
42 |
43 | unit_tests: $(TESTS)
44 | @echo "============================= unit tests (check) ==============================="
45 | @$(TESTS)
46 | else
47 |
48 | unit_tests: $(TESTS)
49 | @echo "unit tests disabled"
50 |
51 | endif
52 |
53 | test: unit_tests
54 | pytest
55 |
56 | clean-local:
57 | rm -f $(SERVER_DIR)/conf/ssl/*
58 | rm -rf *.pyc __pycache__
59 | rm -f data/ssl/valid*
60 | rm -rf $(SERVER_DIR)
61 |
--------------------------------------------------------------------------------
/test/modules/md/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/test/modules/md/__init__.py
--------------------------------------------------------------------------------
/test/modules/md/conftest.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import os
3 | import sys
4 | import pytest
5 |
6 | sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
7 |
8 | from .md_env import MDTestEnv
9 | from .md_acme import MDPebbleRunner, MDBoulderRunner
10 |
11 |
12 | def pytest_report_header(config):
13 | env = MDTestEnv()
14 | return "mod_md: [apache: {aversion}({prefix}), mod_{ssl}, ACME server: {acme}]".format(
15 | prefix=env.prefix,
16 | aversion=env.get_httpd_version(),
17 | ssl=env.ssl_module,
18 | acme=env.acme_server,
19 | )
20 |
21 |
22 | @pytest.fixture(scope="package")
23 | def env(pytestconfig) -> MDTestEnv:
24 | level = logging.INFO
25 | console = logging.StreamHandler()
26 | console.setLevel(level)
27 | console.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
28 | logging.getLogger('').addHandler(console)
29 | logging.getLogger('').setLevel(level=level)
30 | env = MDTestEnv(pytestconfig=pytestconfig)
31 | env.setup_httpd()
32 | env.apache_access_log_clear()
33 | env.httpd_error_log.clear_log()
34 | yield env
35 | env.apache_stop()
36 |
37 |
38 | @pytest.fixture(autouse=True, scope="package")
39 | def _md_package_scope(env):
40 | env.httpd_error_log.add_ignored_lognos([
41 | "AH10085" # There are no SSL certificates configured and no other module contributed any
42 | ])
43 |
44 |
45 | @pytest.fixture(scope="package")
46 | def acme(env):
47 | acme_server = None
48 | if env.acme_server == 'pebble':
49 | acme_server = MDPebbleRunner(env, configs={
50 | 'default': os.path.join(env.gen_dir, 'pebble/pebble.json'),
51 | 'eab': os.path.join(env.gen_dir, 'pebble/pebble-eab.json'),
52 | })
53 | elif env.acme_server == 'boulder':
54 | acme_server = MDBoulderRunner(env)
55 | yield acme_server
56 | if acme_server is not None:
57 | acme_server.stop()
58 |
59 |
--------------------------------------------------------------------------------
/test/modules/md/data/sectigo-demo-root.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDejCCAmKgAwIBAgIQfaNPgBFNflrkSu7M6HnZoTANBgkqhkiG9w0BAQsFADBX
3 | MQswCQYDVQQGEwJDQTELMAkGA1UECBMCT04xDzANBgNVBAcTBk90dGF3YTEQMA4G
4 | A1UEChMHT3B0b3ZpYTEYMBYGA1UEAxMPT3B0b3ZpYSBSb290IENBMB4XDTIxMDgy
5 | NzEyMzUxNloXDTMwMTIzMTIzNTk1OVowVzELMAkGA1UEBhMCQ0ExCzAJBgNVBAgT
6 | Ak9OMQ8wDQYDVQQHEwZPdHRhd2ExEDAOBgNVBAoTB09wdG92aWExGDAWBgNVBAMT
7 | D09wdG92aWEgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
8 | ANN0W9COV5gViQS2XOxJw5+aiuZNk5byuVW+IJ06BpPArz5rlQdv78ze014Ogjwa
9 | KoGeSTdV2tmZpBajLVGlYHuMQUwzRwd8Z9qHk1HU8wPRo3seMesaYcE2y5KEpgcv
10 | 9xFaszEXsFU67ZFBSshsp01/A088aBk4S+quQImwSy1Z5Gm8f+U7GvF9DPCqKhUc
11 | EUXSb8HEY70EAkeEMKsW0sNkcH9hXxVKBdmoB0j2tMnTdogoxP9JLiJxYOk2Y25v
12 | Ci9GzIYzyeiDa/K2nX4Ow2bpCPhR8Y4D8kxQcAC9fD5ZbFgcRitfL2+/OZhyq23J
13 | 4PSAwOmvPWMwXxUuM7V0APUCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgGGMA8GA1Ud
14 | EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMf2hZ3tBqCfzXRBZtAv/2lZQlaRMA0GCSqG
15 | SIb3DQEBCwUAA4IBAQAsNsj8myBHijCE2F4db9ggiwFE1v+aHmsWcou7dhWJK7J7
16 | SkzE78eCXZNt/Ftc7xzHBHu+IExGztXoLwqygGc7tWcHw9/8cgkHpAVa8kCiAVjn
17 | v+LrDwNiNm3JzMT+Q2HXEOZyFadqNxMuvBZI98h8ZzZDfYIgmyP/bYFTtt3FdFtb
18 | DQXq89rlrfBAIN+I0S8KidKXIgWBKC6oiBUrNg/1yHRjuS1+VL3j2jCo6XOco0BH
19 | lw4CR0f19XVblhXhP1yuzyp232z6SV96Gi2ZGZwMGu0cPGW3bj/o9MFW34LbJAXM
20 | Pg1c48PpmQbkA4YOhkI3ocDpF/3nkkeLdzb+p3x1
21 | -----END CERTIFICATE-----
22 |
23 |
--------------------------------------------------------------------------------
/test/modules/md/data/store_migrate/1.0/sample1/accounts/ACME-localhost-0000/account.json:
--------------------------------------------------------------------------------
1 | {
2 | "disabled": false,
3 | "url": "http://localhost:4000/acme/reg/494",
4 | "ca-url": "http://localhost:4000/directory",
5 | "id": "ACME-localhost-0000"
6 | }
--------------------------------------------------------------------------------
/test/modules/md/data/store_migrate/1.0/sample1/accounts/ACME-localhost-0000/account.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN ENCRYPTED PRIVATE KEY-----
2 | MIIJnzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQI0s8pf5rIPTECAggA
3 | MB0GCWCGSAFlAwQBKgQQ2u9SobgmVMhhZxYkXf9kpwSCCVD04Xywr0m+b5f+2aE5
4 | qjGr8y6xlf4NC/+QL6mBCw+9tlsgt7Z9bBt7PR1eMUQ0Bz5a9veBT2JwqGFU8XLv
5 | Anfd4a8ciKRx4kdP7JL08rkKAqPxuwkzMin3TeOJwsoghyvt8zFrXrWEcHyhHd4L
6 | HAoA3ccCxDHH7ydORd7rhEQUOkcjbaJkZi6pzvv+C7kgSTMKYBaI1mlNzX5Oxm6I
7 | ziwmDcOtRgKb17z26zOYWjzbKHGopPlFe9/l32JxTr5UuCihR4NoPGiK08280OWQ
8 | HIwRxQ900AKyJZM1q3RkH4r0xtiik0lX0isx+UIiNEefA4Za/kXLCM7hHVCGwF1z
9 | eE8oX2yNcsX/sw7aVLhRyVDzrT8C5T7+s+K0eV/hfyYXXAZ0z0H+l3f3TRbMlLuq
10 | 1FQnOmEtQy0CbfPGNlzbiK3glp2fc2ZHubTkprMoRTkEKWNiXD0Suhnsll9eV3d2
11 | cHZgsCQyD3LRz+Xj2v6P+fDOcu7IuM7om9GEjNQB1e7dzo6HOSTG2mIsQo6VByJw
12 | syoK1zzC70Jhj/G6aFALTh4dMceoBDyHZzOfiVwC3dGX1QEnNvGD7Za/woMNIx8S
13 | hiqjntDhlXPXCRX/Z/Zvg///6+Ip9FqkCVk74DRWjH9iUzdP7/E1GCyAH2BSdsdc
14 | PnK15p79Ff5TMV91IQmnVV37s57VqXIez2RtuLd530iUk4RtkJ1/PphybHd+JW/n
15 | avMj8gsuWB7RqaBsmbjLmSudSl0DNgy0IJKZs11UifrZmSkaUJH+JJ1W2hLHR980
16 | X75IujUmZasWYkVqq0nvdy8JConCaLd3TT8r8DcO73vZqjFnN+EEHENaEg7F7ig8
17 | xkp0wk4F3u1BEnkwd34aLonZ9DtSK3miDRqlWXqQGESMaQLYQvHUn9q4X57Tyz4T
18 | 9ZVPeLJiuHwCGq6z2BJhgkAlGs7Eqra0pMpjVnRdylTQzx0Q2vLQbrZasyBpReeM
19 | zGdadxRR84PyhAGDGdLKR8VCVFhWX32ZBfqJQOjpyAT30Wu11ZDvEPASuTL4GdcD
20 | o5seucpUZdgzrivvjUhYLkRd0WOjgJyuvtWdillpSiweeGfDAnZvUZUFLd4EMmwH
21 | W+IUr7yIsjNuGZU3NW0pW/L9d9GuwgljP61WKhS6B7hRmx22YU3z2Y7islXiey3m
22 | kZ37mAqdK4EIQca2j9GmBQk7oUz+boYdm4vtk7tJI07LEDI79U95B8x1MpzjuIbj
23 | zlYmH1yw8UefsFrOfjJ4BpkDjVux+J2DmSqCFb5XBcjwWsYiY17niW6Qfrypd6vq
24 | bew1HgbBhdBNQoL1P8uS1fNNwoHmhJc6PNHFFxU3NP91yqB8Igj3khqk9+/VBcCt
25 | 8xRc/1jR5mfAgvaCWyQgIZAsCgTLnvEXy91MG/DKR0ZdOLZJNas+1W9fjhcFvP6S
26 | nNmeMMrIAxaI85RVvnLqPEZhsb9AOlyaf6tKFJiCteyQlie6MOQTKSp4jjSOVW+w
27 | q/WtSZup9zXo8Ek+TnLhD0IJhpIbfR5is5iZaVY7lbcg4pc3Csh/SiMUJ4TJgiPS
28 | /End7LPoRIabRnw4PBtJRNCwf3ilsWUmi95HU3wLAmLpI1AtnbfQi+zva4UJdOTV
29 | HJxNN84ZGuey1gG7qZb3U6WpwzQDKvqTm5jK32nIS/LuNv1qpv0FdAmvulV9wBar
30 | M19CcD5kOlTvNZcf6B4Fkrr+x+Anji/kUV4slIvUbAaU9P4lMO0ORCTg1es7QvI7
31 | v0KRYYSULrO+G2CNYL7fN8Vf5tRpBZ3H1o6u3plw/P86MTQPOskppjK1VKsBBmL2
32 | isdeumWjLpFVr1vWxTm68f88f+iau3BRUkCDQXFEVTN7YuOhpexb6Js0T220HYTS
33 | 9hmeVUnNlXii1BpnxLhBx/0O3heVOLc/C7b7vASg5PljieUQmpuyeJSUAJm1vKrI
34 | p2G/46MgBl+3/NkzLRGepzAH2IGAhhtXEk/zePdRptbVr29+vGDX6IzEWqZ5UYHG
35 | P5JYzaojrmLd0BNYwEbCrRBRHyM4jYFkRERs/kwCh5/Kle/eZpb+bjvIsAs0xcOC
36 | /uRF8RfHW1h8M8Bm9tR+rUX8CTxaIF3IY+N5qSPstNt8xGYLv7uvd+KoK0xVHAm+
37 | FAreqql7koa5D0ncLjTpQGnHiLBKsYmJWC4+TKC+a5m0eKmRgO/r5o+7mmoB9qCZ
38 | bI9GB9HoYeVW/QVWfmoH0W6rbQCmK/VcSB1dGwvz9rKU1DXHhXvGU2k1IAfPX11t
39 | RfwUmmLtrM9tjOWdBh74N4G8UvTk5FGygzJ+Eclm/ABeAChIFU7mLJFejOue/bKq
40 | CRAQul45+CskNyVyZWZvWTFT0UMN290b4E4sjUKoLbFZiA1Y/aU+ruG9iwPJ3yVS
41 | s09VqogNwKBLWYW5TclUzgf71AQTlnZpTudkqwr36ogIAXXaQpE1f6/HLQz3k1PA
42 | WmTaxoM//X00WvTq2UxxSmKf7mNPEg9UZ9m4ZTKe35a//ONxXVjBjtK23yN5MuHY
43 | YrgWF84xlLRPY3Um2ukCsRGb7yZRhlPmOBeYQvRod7BqEA0UmIR+ctnBWDwzSZw7
44 | JWuR+AZdjIfM+Ilh15fokpLI5IFnTAqvTYDoF0185kqYPkjtI2STAWpALA9XJp70
45 | aF/rbdbSrRPFI1+izTIvQjffYftro7EOfCFv62XZm6tj5RLHalfgTcWoUWw81ylL
46 | DOZZaKsv4bOW7HCM47pitFojwzNf9OaHd5VTaSPWts49siF/qCxcG8bwu51picbc
47 | 96H1h3/npNhxDUA5qKzkBK9Bs7panzXt2kNJxPzHEiCjVVGq7t/ei4TZGoSw806D
48 | kNPFhztVoM1k2m7F7lu1EYOwJH/yXKJUgJYIycIoQyRMX7h0jb76U0oOHrdkw3A2
49 | 9Helksl8kqz10td2PZyoj3K/EWu+33cFKgLtC9JrDATR3Lhdo2N3BQQAotW2+Tht
50 | HqHj/UzUoIWcEkzCZeJhRn9WRRbbLeWKwdXBxGl0ZESpJJ2+Ml6QkMkdZSUzDURD
51 | kxYl04U9JXk6vC2hT6780OBLnLivBqIaSUJ72DSkOFnifFoP/OeglWFVkJHWQjQP
52 | aGMcPD/xLLYhdRQlJND9K12FXtsazW2K/V+861y4rJOt6zJGSZwPrQBkLf7QBNAC
53 | DWiLOvp6tLT58pX8TSlplbITcQ==
54 | -----END ENCRYPTED PRIVATE KEY-----
55 |
--------------------------------------------------------------------------------
/test/modules/md/data/store_migrate/1.0/sample1/archive/7007-1502285564.org.1/md.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "7007-1502285564.org",
3 | "domains": [
4 | "7007-1502285564.org"
5 | ],
6 | "contacts": [
7 | "mailto:admin@7007-1502285564.org"
8 | ],
9 | "transitive": 0,
10 | "ca": {
11 | "proto": "ACME",
12 | "url": "http://localhost:4000/directory",
13 | "agreement": "http://boulder:4000/terms/v1"
14 | },
15 | "state": 1,
16 | "renew-mode": 2,
17 | "renew-window": 1209600
18 | }
19 |
--------------------------------------------------------------------------------
/test/modules/md/data/store_migrate/1.0/sample1/domains/7007-1502285564.org/cert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIFkDCCBHigAwIBAgITAP8PGcftT0j60OOjL+Er/XuHrzANBgkqhkiG9w0BAQsF
3 | ADAfMR0wGwYDVQQDDBRoMnBweSBoMmNrZXIgZmFrZSBDQTAeFw0xNzA4MDkxMjMz
4 | MDBaFw0xNzExMDcxMjMzMDBaME0xHDAaBgNVBAMTEzcwMDctMTUwMjI4NTU2NC5v
5 | cmcxLTArBgNVBAUTJGZmMGYxOWM3ZWQ0ZjQ4ZmFkMGUzYTMyZmUxMmJmZDdiODdh
6 | ZjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMHuhVxT9Jpc6EpNAhrq
7 | RqzDJ4tWSG9BtguKZzh3sbY92EE5rqym7wpdb5DG5gwew4iD1R+YizY+99+00qlB
8 | 3kNBUVsJCBnew0apmhPq4jjF8v8t3Qqq0ISn2Sdv5bt5mB9NWeO83h3zT1LW0rTm
9 | 847nwxUuGxlIjLXxsibUvPunMfyGJUshflN5V9/Q3YQBOCnDWy5s4FKN2N34cHFE
10 | IgJo5ToBKZLp9eUaLm03mlfhTFc3/h0AtWwMZ5P2tRRB9EiijqI9nkrVzqyi1QTN
11 | Hn/XfgDgKRCyMp6i5kcK3hCXo4GjOIU0KA91ttf3IeKhXHKzC7ybc4hdJH2rWzoN
12 | srYq6tNZ+cOaa1E/H+v+OMSeIRaRrpM56c3nUssIzbneMIXuLHuOluaaL4baCjYp
13 | Pdc80bUlps06XcnVHysAbsfbtWAtUdzj2l4flVySruGoaqVDudl1GqYoYa+0oReM
14 | Zqd09Q+pCQvDNE+jiVq3An+JA4msux9EMMz7jkAwnl8iiWy0GMuQPsL5gp3TEXGY
15 | Cp1wQlzpmxZSdUZ+J6f4UkFOS/Zn6gS6nSxN8nj3XKbRYRbebPQMwRGYGttCyeZO
16 | dHiUY/3gQBUdpcMBJhAa5GFoabK0J5XPmK2E1P9cGQo7DbNn+Skojnz2WuUtCuyo
17 | m9la14Ruca9V8NmjBsu+4mXvAgMBAAGjggGVMIIBkTAOBgNVHQ8BAf8EBAMCBaAw
18 | HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYD
19 | VR0OBBYEFH426IYgY0KXUe9cLMZ3d8tipsDkMB8GA1UdIwQYMBaAFPt4TxL5YBWD
20 | LJ8XfzQZsy426kGJMGYGCCsGAQUFBwEBBFowWDAiBggrBgEFBQcwAYYWaHR0cDov
21 | LzEyNy4wLjAuMTo0MDAyLzAyBggrBgEFBQcwAoYmaHR0cDovLzEyNy4wLjAuMTo0
22 | MDAwL2FjbWUvaXNzdWVyLWNlcnQwHgYDVR0RBBcwFYITNzAwNy0xNTAyMjg1NTY0
23 | Lm9yZzAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8vZXhhbXBsZS5jb20vY3JsMGEG
24 | A1UdIARaMFgwCAYGZ4EMAQIBMEwGAyoDBDBFMCIGCCsGAQUFBwIBFhZodHRwOi8v
25 | ZXhhbXBsZS5jb20vY3BzMB8GCCsGAQUFBwICMBMMEURvIFdoYXQgVGhvdSBXaWx0
26 | MA0GCSqGSIb3DQEBCwUAA4IBAQBfqLXSJZ5Izs2I44cXWrAto631aTylValp0Fiy
27 | Zz1dj00FS6XN5DGtfIyq7Ymd3MMiOZCLkTOMMb7BrJAvcgeJteKwdk3ffXEDyKH0
28 | 1ttXK7l46trEyGOB+f9PMMKxVMyhDhGKyb6ro4Y5WTK/w4862soqKcP1SjHvk65u
29 | lIkFws1fWYYzqPLKLij2ILm+4NjdGIl8qPQWP2PtbOaDTFspJBz6hvLmqRgmjVVv
30 | cENwBUML4LCkVY3TUqoBHXDhpocTZlVeAVRVsroosboQJlY5nIKz6cOjilILn4cT
31 | hgEKa5IRwK5lUveCoeQtYUyLoyp5ncbota+UxNxCnkl/0veK
32 | -----END CERTIFICATE-----
33 |
--------------------------------------------------------------------------------
/test/modules/md/data/store_migrate/1.0/sample1/domains/7007-1502285564.org/chain.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIEijCCA3KgAwIBAgICEk0wDQYJKoZIhvcNAQELBQAwKzEpMCcGA1UEAwwgY2Fj
3 | a2xpbmcgY3J5cHRvZ3JhcGhlciBmYWtlIFJPT1QwHhcNMTUxMDIxMjAxMTUyWhcN
4 | MjAxMDE5MjAxMTUyWjAfMR0wGwYDVQQDExRoYXBweSBoYWNrZXIgZmFrZSBDQTCC
5 | ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMIKR3maBcUSsncXYzQT13D5
6 | Nr+Z3mLxMMh3TUdt6sACmqbJ0btRlgXfMtNLM2OU1I6a3Ju+tIZSdn2v21JBwvxU
7 | zpZQ4zy2cimIiMQDZCQHJwzC9GZn8HaW091iz9H0Go3A7WDXwYNmsdLNRi00o14U
8 | joaVqaPsYrZWvRKaIRqaU0hHmS0AWwQSvN/93iMIXuyiwywmkwKbWnnxCQ/gsctK
9 | FUtcNrwEx9Wgj6KlhwDTyI1QWSBbxVYNyUgPFzKxrSmwMO0yNff7ho+QT9x5+Y/7
10 | XE59S4Mc4ZXxcXKew/gSlN9U5mvT+D2BhDtkCupdfsZNCQWp27A+b/DmrFI9NqsC
11 | AwEAAaOCAcIwggG+MBIGA1UdEwEB/wQIMAYBAf8CAQAwQwYDVR0eBDwwOqE4MAaC
12 | BC5taWwwCocIAAAAAAAAAAAwIocgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
13 | AAAAAAAwDgYDVR0PAQH/BAQDAgGGMH8GCCsGAQUFBwEBBHMwcTAyBggrBgEFBQcw
14 | AYYmaHR0cDovL2lzcmcudHJ1c3RpZC5vY3NwLmlkZW50cnVzdC5jb20wOwYIKwYB
15 | BQUHMAKGL2h0dHA6Ly9hcHBzLmlkZW50cnVzdC5jb20vcm9vdHMvZHN0cm9vdGNh
16 | eDMucDdjMB8GA1UdIwQYMBaAFOmkP+6epeby1dd5YDyTpi4kjpeqMFQGA1UdIARN
17 | MEswCAYGZ4EMAQIBMD8GCysGAQQBgt8TAQEBMDAwLgYIKwYBBQUHAgEWImh0dHA6
18 | Ly9jcHMucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcwPAYDVR0fBDUwMzAxoC+gLYYr
19 | aHR0cDovL2NybC5pZGVudHJ1c3QuY29tL0RTVFJPT1RDQVgzQ1JMLmNybDAdBgNV
20 | HQ4EFgQU+3hPEvlgFYMsnxd/NBmzLjbqQYkwDQYJKoZIhvcNAQELBQADggEBAA0Y
21 | AeLXOklx4hhCikUUl+BdnFfn1g0W5AiQLVNIOL6PnqXu0wjnhNyhqdwnfhYMnoy4
22 | idRh4lB6pz8Gf9pnlLd/DnWSV3gS+/I/mAl1dCkKby6H2V790e6IHmIK2KYm3jm+
23 | U++FIdGpBdsQTSdmiX/rAyuxMDM0adMkNBwTfQmZQCz6nGHw1QcSPZMvZpsC8Skv
24 | ekzxsjF1otOrMUPNPQvtTWrVx8GlR2qfx/4xbQa1v2frNvFBCmO59goz+jnWvfTt
25 | j2NjwDZ7vlMBsPm16dbKYC840uvRoZjxqsdc3ChCZjqimFqlNG/xoPA8+dTicZzC
26 | XE9ijPIcvW6y1aa3bGw=
27 | -----END CERTIFICATE-----
28 |
--------------------------------------------------------------------------------
/test/modules/md/data/store_migrate/1.0/sample1/domains/7007-1502285564.org/md.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "7007-1502285564.org",
3 | "domains": [
4 | "7007-1502285564.org"
5 | ],
6 | "contacts": [
7 | "mailto:admin@7007-1502285564.org"
8 | ],
9 | "transitive": 0,
10 | "ca": {
11 | "account": "ACME-localhost-0000",
12 | "proto": "ACME",
13 | "url": "http://localhost:4000/directory",
14 | "agreement": "http://boulder:4000/terms/v1"
15 | },
16 | "cert": {
17 | "url": "http://localhost:4000/acme/cert/ff0f19c7ed4f48fad0e3a32fe12bfd7b87af",
18 | "expires": "Tue, 07 Nov 2017 12:33:00 GMT"
19 | },
20 | "state": 2,
21 | "renew-mode": 2,
22 | "renew-window": 1209600
23 | }
24 |
--------------------------------------------------------------------------------
/test/modules/md/data/store_migrate/1.0/sample1/domains/7007-1502285564.org/pkey.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDB7oVcU/SaXOhK
3 | TQIa6kaswyeLVkhvQbYLimc4d7G2PdhBOa6spu8KXW+QxuYMHsOIg9UfmIs2Pvff
4 | tNKpQd5DQVFbCQgZ3sNGqZoT6uI4xfL/Ld0KqtCEp9knb+W7eZgfTVnjvN4d809S
5 | 1tK05vOO58MVLhsZSIy18bIm1Lz7pzH8hiVLIX5TeVff0N2EATgpw1subOBSjdjd
6 | +HBxRCICaOU6ASmS6fXlGi5tN5pX4UxXN/4dALVsDGeT9rUUQfRIoo6iPZ5K1c6s
7 | otUEzR5/134A4CkQsjKeouZHCt4Ql6OBoziFNCgPdbbX9yHioVxyswu8m3OIXSR9
8 | q1s6DbK2KurTWfnDmmtRPx/r/jjEniEWka6TOenN51LLCM253jCF7ix7jpbmmi+G
9 | 2go2KT3XPNG1JabNOl3J1R8rAG7H27VgLVHc49peH5Vckq7hqGqlQ7nZdRqmKGGv
10 | tKEXjGandPUPqQkLwzRPo4latwJ/iQOJrLsfRDDM+45AMJ5fIolstBjLkD7C+YKd
11 | 0xFxmAqdcEJc6ZsWUnVGfien+FJBTkv2Z+oEup0sTfJ491ym0WEW3mz0DMERmBrb
12 | QsnmTnR4lGP94EAVHaXDASYQGuRhaGmytCeVz5ithNT/XBkKOw2zZ/kpKI589lrl
13 | LQrsqJvZWteEbnGvVfDZowbLvuJl7wIDAQABAoICAQCVSZob0v1O/wpKeDGQqpwx
14 | TiHY31jvXHRZOffvviRtl/ora84NVoxZPEgv+Q0Kc3wuUN31bqZr4dlKupYYeX4x
15 | 48xO+grkb1l/wfu8LWpsLeW7joDEP245UESYWUlOInJ6Vj9GUxPhlnWP3ZNicw83
16 | CS5h1ZZCxlibjy2HOukoCDMwo8t9pJDsjVKaFt0PSykC7UH54RJmOo+hgCh+6OYN
17 | WNZs6owobjY+YQMwTEdiMytjUNUrWmpOfNYXTyliKMt2RrzqI+kAzspElyzIf2Zl
18 | H2v+HJFAKw1QlTITqkf8Gd9iYlWWJOpZzFIuui25mmHiYfY9AKXVaW4313tomzbg
19 | L9Muc0pCmR8ge/hsC+C2QkVhHRFThakd5zU8rOEeXClzLKg1tjSVwcyNllXwd3Uy
20 | gQRtDqAWcWhXj2pqPzLc4v/wobjPE+xEpAbvDBvEof1fMy1PBeyKq7T4mIxswuWF
21 | takm9/Bt15K2TNBc7qNQV2x+MCS0Bi2Hd1yjLbIHllBDQR2ZsHRw1D38ckbL7ATE
22 | yDwnzI2gxlYYV7K/iQG9XkM54Ra5tNOFYv9GiCw+JPrLcQ5qmGsCCu6lfktMC8pN
23 | 7VQRbHt60ZKaunE1muwWDmyYzP106qUXMw6nIVMyqX0ywTEPAgtRgWcucLWR33DD
24 | k1OBcq2tOceaZjA5Pbi4sQKCAQEA+MbI4HEbROlsPeQ7VMOoAHjJPWuhDNXqnz4Q
25 | c4z3X+W61TAWZINRENYDZd3c7D7wOWb9VBA+o62xrzYviul9qhTAjZ8dRfxagJpH
26 | OxNY348HNj+IxONj3RXr/7tfOXtzcjiFwzn85oPLRM56XfjYZ5lUgQBSEauXOue5
27 | +bpNBvrYZLPm7i5BM8RpBElH2wtCizLAE9BrKYUqTYWyl76miPfpeSVMv2JOpUwp
28 | josVrAWAOoQHeIrCLmSF43oqmtzJ9Aq1r/VeOQB/3TT4E0RhWhDWOg3zNuA20w+E
29 | VuKyl4J/XLo6T86Zc/PM4+vb8zPztjZHQVJj58Iq7N4/y5cBfQKCAQEAx5AP10sw
30 | C4kCwU/yXORhimMPlRldKx2h+8Ha/0whTkehXaJ0synCV0ZLh7jSgfe81Zx5/3RK
31 | KKRWVx7+wmQiOqfSIBJN4xWdpVDS7yndk/FW8sYqT1v2sgr2t1u41bQAY3qzezsK
32 | elNsjbRsUCVvVu9HZ5zH7Pvmf0Ma8P2t8EioQWJ2ptgF6imTXIrQORJPBqDEzp6W
33 | EjiHC9kuZ2E+uPGl+6oQcxRUjtFkxnI9LgpOQCjNNIhW6cEhJxV3z8YIUnUyd7vd
34 | i0eEfhKF+DXzrqbtve63iGGU7TFMiiNF59hPxKHkPvHnUlXNZjJ8om9M579i/9fm
35 | OHYWaWFuzb6g2wKCAQAIZ37FxkxriY80kA9JD8sPKQVzY71vF5Lzij84CB0bSkGD
36 | jjpTbvRAI1q+CD68ZGvtJIOOYXYcRXPpPWVhxf2Oz2Cp6CQvBxVvnsalQkQQWV6f
37 | AIp4TE5FW8Y7P3M6F+eQhkROkhjvGKi3TFpp7kwxQ8bNDNu46RkUzltECn0rrTG+
38 | RS2aAkoFm68IjAk3Zyv6U96VTMcyAeOp9shPxAsQOX/TreTn2kRZ5TbKL/ytcQoh
39 | 7+/orJdexdqYErp5vNe9vNbieOGT/2ZSbMWssPSw/DygfXQn+G8htjZ8UPBDmg7/
40 | bPMnWw1oE2ZqlL87ehfTogXKOSRS4gZdNizljdZpAoIBADxSfZdUcOdruNt6MQaH
41 | Ojy8iN9G1XTM9kPFa080UfT5jfthuejWPJpo8zfJVEhY/EmNjQr8udXjJv4armNQ
42 | JVCZndh37/cud4KbFceZXhL0JpYn9G4cnEthKQZvwUVHrb5kPpCHXjlvsiZ7XSo0
43 | xpz+oxTcvUoTMq9RN3mVFNjG/aUWAEuajN8lRhf5FcvKjvyv6A2UvkQvthKMyYwS
44 | RwVcdhHGbEZ85Lpu7QlXSsr57oFSVAUHGU57RGwt/xNdBvL13hV3QhZxvcjmDHzk
45 | wg4PA1ogKHYfGQdBmaM/2kekiSgkz3t/X67xpK65oBbxkcuTfHddaYezmj6sZvPm
46 | JXUCggEBAO37OxP7B66FQghuBkfui8sPymY2oSFQIb3IRO5A17/wp9yW1f9X4Bu4
47 | dh7ln+6IEURZyldAZcVRSHbjrL8VWXtS86eDttnKD7L46BbqAytckc/pebA/5bu0
48 | tjsM8ulayPGuJzEl/g1F1bU1eduXkmq/O7636S0Q1KCVHldn9qNgkowfjpzANHNs
49 | ksSwxMIY8n4U2kckMmfCj2B6UrnqQ6Bs7IaijQJ5u/mGYke+gKEGQ99esx2Ts1Vl
50 | w8WDaDUOwHEywuFyqtGJzizX8BazIzwmSCh8hpedDtFVVnfjszLnf3Y+FOrb9XlM
51 | Wc8hH7giOwSubI2D2mauspM5CZlez7A=
52 | -----END PRIVATE KEY-----
53 |
--------------------------------------------------------------------------------
/test/modules/md/data/store_migrate/1.0/sample1/httpd.json:
--------------------------------------------------------------------------------
1 | {
2 | "proto": {
3 | "http": true,
4 | "https": true
5 | }
6 | }
--------------------------------------------------------------------------------
/test/modules/md/data/store_migrate/1.0/sample1/md_store.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.6.1-git",
3 | "store": {
4 | "version": 1.0
5 | },
6 | "key": "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmNkZWZnaGlqa2xtbm9wcXJzdHV2"
7 | }
--------------------------------------------------------------------------------
/test/modules/md/data/test_920/002.pubcert:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIFYDCCBEigAwIBAgISAwOcRk1FTt55/NLK6Fn2aPJpMA0GCSqGSIb3DQEBCwUA
3 | MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
4 | ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA1MzExNjA2MzVaFw0x
5 | OTA4MjkxNjA2MzVaMBYxFDASBgNVBAMTC2Vpc3Npbmcub3JnMIIBIjANBgkqhkiG
6 | 9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9d5xZdknImIPfmiUaiiRhHLx4bvazWRTgA2+
7 | etRNKr42MRjkuLbAhvxGjhw4El0GJlbngKTfiSK0Vq0idW/ehUr++czRSDrRVfqq
8 | qcI/F4NXLIbIZfmR7/vG0IP8Xc8D9VyQCX0uDapCvw+A/U46p0VOZz4bIB/bl0BW
9 | /mqBvVhBU9owskUcPjwwI/tK6My933CUVKXuFpPZ4V7zoY0/8Xa6JmWC2q1+7XmE
10 | h51hPnU35dYH1bA7WblX8rVxnEPCyCOgABVLKb6NhWfTCEqy+yzr32KsoSR1xqe4
11 | T2EeTcoamwF2yhz2zRC4glX0LM4inJ1/ZOQ+nKbFZTOPVWEnLQIDAQABo4ICcjCC
12 | Am4wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
13 | AjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTfO7pZGPLsa0NuPZMG4NGlr1TaWjAf
14 | BgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcBAQRjMGEw
15 | LgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlwdC5vcmcw
16 | LwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlwdC5vcmcv
17 | MCcGA1UdEQQgMB6CC2Vpc3Npbmcub3Jngg93d3cuZWlzc2luZy5vcmcwTAYDVR0g
18 | BEUwQzAIBgZngQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0
19 | cDovL2Nwcy5sZXRzZW5jcnlwdC5vcmcwggEFBgorBgEEAdZ5AgQCBIH2BIHzAPEA
20 | dwB0ftqDMa0zEJEhnM4lT0Jwwr/9XkIgCMY3NXnmEHvMVgAAAWsO24QlAAAEAwBI
21 | MEYCIQD8yd2uHl2DNgvnBkSiA8vsK5pOv204NixI9F89LWERwgIhAPMLLiZkFG2h
22 | DTpEwF50BbZ+laYH8VP03Teq5csk2lX0AHYAKTxRllTIOWW6qlD8WAfUt2+/WHop
23 | ctykwwz05UVH9HgAAAFrDtuEFgAABAMARzBFAiEA3bYpKSNigSe0HuDyH/kerTW2
24 | 55ugvODp6d+vNbNmgZoCIGTd4cio769BTKfLJTqNbjc9sKK9T7XkHUO4JgQdY6Nq
25 | MA0GCSqGSIb3DQEBCwUAA4IBAQBeatZxh8leVmeFE/IYTKKqHyZqTccJKdugXIOr
26 | uIF6sLup/8Fv/2N0wZc+edkj+NCyWhxxkZULyW6xhlL7rtzcwLYbQBSxKvT4Utur
27 | 01a5bwhM62MdMjzkFgCCa5nRKPQ7bc684RrUFNi94d0KSb5ArFv8wovqPW7jbmFp
28 | X50dYKCE+wohFPHcsQapnV0lXK4+5qJZSZkp/pHANdndLCvFfzRHhV4nqRA12G2T
29 | VVWjdHN6ShL2uykJVAnSBhu/XD4mh79Yq9TQtS1DHfP3HcKstLqR0nrwBFaB6087
30 | jXfIpJ46yObq001qHeUMhT+B3WI2YPp/hY7u8A9+hCmDyyq8
31 | -----END CERTIFICATE-----
32 | -----BEGIN CERTIFICATE-----
33 | MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
34 | MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
35 | DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
36 | SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
37 | GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
38 | AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
39 | q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
40 | SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
41 | Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
42 | a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
43 | /PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
44 | AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
45 | CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
46 | bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
47 | c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
48 | VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
49 | ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
50 | MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
51 | Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
52 | AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
53 | uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
54 | wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
55 | X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
56 | PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
57 | KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
58 | -----END CERTIFICATE-----
59 |
--------------------------------------------------------------------------------
/test/modules/md/data/test_conf_validate/test_014.conf:
--------------------------------------------------------------------------------
1 | # global server name as managed domain name
2 |
3 | MDomain resistance.fritz.box www.example2.org
4 |
5 |
6 | ServerName www.example2.org
7 |
8 |
9 |
--------------------------------------------------------------------------------
/test/modules/md/data/test_drive/test1.example.org.conf:
--------------------------------------------------------------------------------
1 | # A setup that required manual driving, e.g. invoking a2md outside apache
2 | #
3 | MDRenewMode manual
4 |
5 | MDomain test1.not-forbidden.org www.test1.not-forbidden.org mail.test1.not-forbidden.org
6 |
7 |
--------------------------------------------------------------------------------
/test/modules/md/data/test_roundtrip/temp.conf:
--------------------------------------------------------------------------------
1 | MDDriveMode manual
2 | MDCertificateAuthority http://localhost:4000/directory
3 | MDCertificateProtocol ACME
4 | MDCertificateAgreement http://boulder:4000/terms/v1
5 |
6 | ServerAdmin mailto:admin@test102-1499953506.org
7 |
8 | ManagedDomain test102-1499953506.org test-a.test102-1499953506.org test-b.test102-1499953506.org
9 |
10 |
11 | ServerName test-a.test102-1499953506.org
12 | DocumentRoot htdocs/a
13 |
14 | SSLEngine on
15 | SSLCertificateFile /Users/sei/projects/mod_md/test/gen/apache/md/domains/test102-1499953506.org/cert.pem
16 | SSLCertificateKeyFile /Users/sei/projects/mod_md/test/gen/apache/md/domains/test102-1499953506.org/pkey.pem
17 |
18 |
19 |
20 | ServerName test-b.test102-1499953506.org
21 | DocumentRoot htdocs/b
22 |
23 | SSLEngine on
24 | SSLCertificateFile /Users/sei/projects/mod_md/test/gen/apache/md/domains/test102-1499953506.org/cert.pem
25 | SSLCertificateKeyFile /Users/sei/projects/mod_md/test/gen/apache/md/domains/test102-1499953506.org/pkey.pem
26 |
27 |
28 |
--------------------------------------------------------------------------------
/test/modules/md/dns01.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import subprocess
4 | import sys
5 |
6 | curl = "curl"
7 | challtestsrv = "localhost:8055"
8 |
9 |
10 | def run(args):
11 | sys.stderr.write(f"run: {' '.join(args)}\n")
12 | p = subprocess.Popen(args, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
13 | output, errput = p.communicate(None)
14 | rv = p.wait()
15 | if rv != 0:
16 | sys.stderr.write(errput.decode())
17 | sys.stdout.write(output.decode())
18 | return rv
19 |
20 |
21 | def teardown(domain):
22 | rv = run([curl, '-s', '-d', f'{{"host":"_acme-challenge.{domain}"}}',
23 | f'{challtestsrv}/clear-txt'])
24 | if rv == 0:
25 | rv = run([curl, '-s', '-d', f'{{"host":"{domain}"}}',
26 | f'{challtestsrv}/set-txt'])
27 | return rv
28 |
29 |
30 | def setup(domain, challenge):
31 | teardown(domain)
32 | rv = run([curl, '-s', '-d', f'{{"host":"{domain}", "addresses":["127.0.0.1"]}}',
33 | f'{challtestsrv}/set-txt'])
34 | if rv == 0:
35 | rv = run([curl, '-s', '-d', f'{{"host":"_acme-challenge.{domain}.", "value":"{challenge}"}}',
36 | f'{challtestsrv}/set-txt'])
37 | return rv
38 |
39 |
40 | def main(argv):
41 | if len(argv) > 1:
42 | if argv[1] == 'setup':
43 | if len(argv) != 4:
44 | sys.stderr.write("wrong number of arguments: dns01.py setup \n")
45 | sys.exit(2)
46 | rv = setup(argv[2], argv[3])
47 | elif argv[1] == 'teardown':
48 | if len(argv) != 3:
49 | sys.stderr.write("wrong number of arguments: dns01.py teardown \n")
50 | sys.exit(1)
51 | rv = teardown(argv[2])
52 | else:
53 | sys.stderr.write(f"unknown option {argv[1]}\n")
54 | rv = 2
55 | else:
56 | sys.stderr.write("dns01.py wrong number of arguments\n")
57 | rv = 2
58 | sys.exit(rv)
59 |
60 |
61 | if __name__ == "__main__":
62 | main(sys.argv)
63 |
--------------------------------------------------------------------------------
/test/modules/md/dns01_v2.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import subprocess
4 | import sys
5 |
6 | curl = "curl"
7 | challtestsrv = "localhost:8055"
8 |
9 |
10 | def run(args):
11 | sys.stderr.write(f"run: {' '.join(args)}\n")
12 | p = subprocess.Popen(args, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
13 | output, errput = p.communicate(None)
14 | rv = p.wait()
15 | if rv != 0:
16 | sys.stderr.write(errput.decode())
17 | sys.stdout.write(output.decode())
18 | return rv
19 |
20 |
21 | def teardown(domain):
22 | rv = run([curl, '-s', '-d', f'{{"host":"_acme-challenge.{domain}"}}',
23 | f'{challtestsrv}/clear-txt'])
24 | if rv == 0:
25 | rv = run([curl, '-s', '-d', f'{{"host":"{domain}"}}',
26 | f'{challtestsrv}/set-txt'])
27 | return rv
28 |
29 |
30 | def setup(domain, challenge):
31 | teardown(domain)
32 | rv = run([curl, '-s', '-d', f'{{"host":"{domain}", "addresses":["127.0.0.1"]}}',
33 | f'{challtestsrv}/set-txt'])
34 | if rv == 0:
35 | rv = run([curl, '-s', '-d', f'{{"host":"_acme-challenge.{domain}.", "value":"{challenge}"}}',
36 | f'{challtestsrv}/set-txt'])
37 | return rv
38 |
39 |
40 | def main(argv):
41 | if len(argv) > 1:
42 | if argv[1] == 'setup':
43 | if len(argv) != 4:
44 | sys.stderr.write("wrong number of arguments: dns01.py setup \n")
45 | sys.exit(2)
46 | rv = setup(argv[2], argv[3])
47 | elif argv[1] == 'teardown':
48 | if len(argv) != 4:
49 | sys.stderr.write("wrong number of arguments: dns01.py teardown \n")
50 | sys.exit(1)
51 | rv = teardown(argv[2])
52 | else:
53 | sys.stderr.write(f"unknown option {argv[1]}\n")
54 | rv = 2
55 | else:
56 | sys.stderr.write("dns01.py wrong number of arguments\n")
57 | rv = 2
58 | sys.exit(rv)
59 |
60 |
61 | if __name__ == "__main__":
62 | main(sys.argv)
63 |
--------------------------------------------------------------------------------
/test/modules/md/http_challenge_foobar.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import os
3 | import re
4 | import sys
5 |
6 |
7 | def main(argv):
8 | if len(argv) < 4:
9 | sys.stderr.write(f"{argv[0]} without too few arguments")
10 | sys.exit(7)
11 | store_dir = argv[1]
12 | event = argv[2]
13 | mdomain = argv[3]
14 | m = re.match(r'(\S+):(\S+):(\S+)', event)
15 | if m and 'challenge-setup' == m.group(1) and 'http-01' == m.group(2):
16 | dns_name = m.group(3)
17 | challenge_file = f"{store_dir}/challenges/{dns_name}/acme-http-01.txt"
18 | if not os.path.isfile(challenge_file):
19 | sys.stderr.write(f"{argv[0]} does not exist: {challenge_file}")
20 | sys.exit(8)
21 | with open(challenge_file, 'w') as fd:
22 | fd.write('this_is_an_invalidated_http-01_challenge')
23 | sys.exit(0)
24 |
25 |
26 | if __name__ == "__main__":
27 | main(sys.argv)
28 |
--------------------------------------------------------------------------------
/test/modules/md/md_conf.py:
--------------------------------------------------------------------------------
1 | from .md_env import MDTestEnv
2 | from pyhttpd.conf import HttpdConf
3 |
4 |
5 | class MDConf(HttpdConf):
6 |
7 | def __init__(self, env: MDTestEnv, text=None, std_ports=True,
8 | local_ca=True, std_vhosts=True, proxy=False,
9 | admin=None):
10 | super().__init__(env=env)
11 |
12 | if admin is None:
13 | admin = f"admin@{env.http_tld}"
14 | if len(admin.strip()):
15 | self.add_admin(admin)
16 | self.add([
17 | "MDRetryDelay 1s", # speed up testing a little
18 | ])
19 | if local_ca:
20 | self.add([
21 | f"MDCertificateAuthority {env.acme_url}",
22 | f"MDCertificateAgreement accepted",
23 | f"MDCACertificateFile {env.server_dir}/acme-ca.pem",
24 | "",
25 | ])
26 | if std_ports:
27 | self.add(f"MDPortMap 80:{env.http_port} 443:{env.https_port}")
28 | if env.ssl_module == "mod_tls":
29 | self.add(f"TLSListen {env.https_port}")
30 | self.add([
31 | "",
32 | " SetHandler server-status",
33 | "",
34 | "",
35 | " SetHandler md-status",
36 | "",
37 | ])
38 | if std_vhosts:
39 | self.add_vhost_test1()
40 | if proxy:
41 | self.add([
42 | f"Listen {self.env.proxy_port}",
43 | f"",
44 | " ProxyRequests On",
45 | " ProxyVia On",
46 | " # be totally open",
47 | " AllowCONNECT 0-56535",
48 | " ",
49 | " # No require or other restrictions, this is just a test server",
50 | " ",
51 | "",
52 | ])
53 | if text is not None:
54 | self.add(text)
55 |
56 | def add_drive_mode(self, mode):
57 | self.add("MDRenewMode \"%s\"\n" % mode)
58 |
59 | def add_renew_window(self, window):
60 | self.add("MDRenewWindow %s\n" % window)
61 |
62 | def add_private_key(self, key_type, key_params):
63 | self.add("MDPrivateKeys %s %s\n" % (key_type, " ".join(map(lambda p: str(p), key_params))))
64 |
65 | def add_admin(self, email):
66 | self.add(f"ServerAdmin mailto:{email}")
67 |
68 | def add_md(self, domains):
69 | dlist = " ".join(domains) # without quotes
70 | self.add(f"MDomain {dlist}\n")
71 |
72 | def start_md(self, domains):
73 | dlist = " ".join([f"\"{d}\"" for d in domains]) # with quotes, #257
74 | self.add(f"\n")
75 |
76 | def end_md(self):
77 | self.add("\n")
78 |
79 | def start_md2(self, domains):
80 | self.add("\n" % " ".join(domains))
81 |
82 | def end_md2(self):
83 | self.add("\n")
84 |
--------------------------------------------------------------------------------
/test/modules/md/message.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import os
4 | import sys
5 |
6 |
7 | def main(argv):
8 | if len(argv) > 2:
9 | cmd = argv[2]
10 | if 'renewing' != cmd:
11 | f1 = open(argv[1], 'a+')
12 | f1.write(f'{argv}\n')
13 | if 'MD_VERSION' in os.environ:
14 | f1.write(f'MD_VERSION={os.environ["MD_VERSION"]}\n')
15 | if 'MD_STORE' in os.environ:
16 | f1.write(f'MD_STORE={os.environ["MD_STORE"]}\n')
17 | f1.close()
18 | sys.stderr.write("done, all fine.\n")
19 | sys.exit(0)
20 | else:
21 | sys.stderr.write(f"{argv[0]} without arguments")
22 | sys.exit(7)
23 |
24 |
25 | if __name__ == "__main__":
26 | main(sys.argv)
27 |
--------------------------------------------------------------------------------
/test/modules/md/msg_fail_on.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import os
4 | import sys
5 |
6 |
7 | def main(argv):
8 | if len(argv) > 3:
9 | log = argv[1]
10 | fail_on = argv[2]
11 | cmd = argv[3]
12 | domain = argv[4]
13 | if 'renewing' != cmd:
14 | f1 = open(log, 'a+')
15 | f1.write(f"{[argv[0], log, cmd, domain]}\n")
16 | f1.close()
17 | if cmd.startswith(fail_on):
18 | sys.stderr.write(f"failing on: {cmd}\n")
19 | sys.exit(1)
20 | sys.stderr.write("done, all fine.\n")
21 | sys.exit(0)
22 | else:
23 | sys.stderr.write("%s without arguments" % (argv[0]))
24 | sys.exit(7)
25 |
26 |
27 | if __name__ == "__main__":
28 | main(sys.argv)
29 |
--------------------------------------------------------------------------------
/test/modules/md/notifail.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import sys
4 |
5 |
6 | def main(argv):
7 | if len(argv) > 1:
8 | msg = argv[2] if len(argv) > 2 else None
9 | # fail on later messaging stages, not the initial 'renewing' one.
10 | # we have test_901_030 that check that later stages are not invoked
11 | # when misconfigurations are detected early.
12 | sys.exit(1 if msg != "renewing" else 0)
13 |
14 |
15 | if __name__ == "__main__":
16 | main(sys.argv)
17 |
--------------------------------------------------------------------------------
/test/modules/md/notify.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import sys
4 |
5 |
6 | def main(argv):
7 | if len(argv) > 2:
8 | with open(argv[1], 'a+') as f1:
9 | f1.write(f"{argv}\n")
10 | sys.stderr.write("done, all fine.\n")
11 | sys.exit(0)
12 | else:
13 | sys.stderr.write(f"{argv[0]} without arguments")
14 | sys.exit(7)
15 |
16 |
17 | if __name__ == "__main__":
18 | main(sys.argv)
19 |
--------------------------------------------------------------------------------
/test/modules/md/pebble/pebble-eab.json.template:
--------------------------------------------------------------------------------
1 | {
2 | "pebble": {
3 | "listenAddress": "0.0.0.0:14000",
4 | "managementListenAddress": "0.0.0.0:15000",
5 | "certificate": "${server_dir}/ca/localhost.rsa2048.cert.pem",
6 | "privateKey": "${server_dir}/ca/localhost.rsa2048.pkey.pem",
7 | "httpPort": ${http_port},
8 | "tlsPort": ${https_port},
9 | "ocspResponderURL": "",
10 | "externalAccountBindingRequired": true,
11 | "externalAccountMACKeys": {
12 | "kid-1": "zWNDZM6eQGHWpSRTPal5eIUYFTu7EajVIoguysqZ9wG44nMEtx3MUAsUDkMTQ12W",
13 | "kid-2": "b10lLJs8l1GPIzsLP0s6pMt8O0XVGnfTaCeROxQM0BIt2XrJMDHJZBM5NuQmQJQH"
14 | },
15 | "profiles": {
16 | "default": {
17 | "description": "The profile you know and love",
18 | "validityPeriod": 7776000
19 | },
20 | "shortlived": {
21 | "description": "A short-lived cert profile, without actual enforcement",
22 | "validityPeriod": 518400
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/test/modules/md/pebble/pebble.json.template:
--------------------------------------------------------------------------------
1 | {
2 | "pebble": {
3 | "listenAddress": "0.0.0.0:14000",
4 | "managementListenAddress": "0.0.0.0:15000",
5 | "certificate": "${server_dir}/ca/localhost.rsa2048.cert.pem",
6 | "privateKey": "${server_dir}/ca/localhost.rsa2048.pkey.pem",
7 | "httpPort": ${http_port},
8 | "tlsPort": ${https_port},
9 | "ocspResponderURL": "",
10 | "externalAccountBindingRequired": false,
11 | "domainBlocklist": ["blocked-domain.example"],
12 | "retryAfter": {
13 | "authz": 3,
14 | "order": 5
15 | },
16 | "profiles": {
17 | "default": {
18 | "description": "The profile you know and love",
19 | "validityPeriod": 7776000
20 | },
21 | "shortlived": {
22 | "description": "A short-lived cert profile, without actual enforcement",
23 | "validityPeriod": 518400
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test/modules/md/test_010_store_migrate.py:
--------------------------------------------------------------------------------
1 | # test mod_md acme terms-of-service handling
2 |
3 | import os
4 | import pytest
5 |
6 | from .md_conf import MDConf
7 | from .md_env import MDTestEnv
8 |
9 |
10 | @pytest.mark.skipif(condition=not MDTestEnv.has_a2md(), reason="no a2md available")
11 | class TestStoreMigrate:
12 |
13 | @pytest.fixture(autouse=True, scope='class')
14 | def _class_scope(self, env):
15 | MDConf(env).install()
16 | assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
17 |
18 | # install old store, start a2md list, check files afterwards
19 | def test_md_010_000(self, env):
20 | domain = "7007-1502285564.org"
21 | env.replace_store(os.path.join(env.test_dir, "../modules/md/data/store_migrate/1.0/sample1"))
22 | #
23 | # use 1.0 file name for private key
24 | fpkey_1_0 = os.path.join(env.store_dir, 'domains', domain, 'pkey.pem')
25 | fpkey_1_1 = os.path.join(env.store_dir, 'domains', domain, 'privkey.pem')
26 | cert_1_0 = os.path.join(env.store_dir, 'domains', domain, 'cert.pem')
27 | cert_1_1 = os.path.join(env.store_dir, 'domains', domain, 'pubcert.pem')
28 | chain_1_0 = os.path.join(env.store_dir, 'domains', domain, 'chain.pem')
29 | #
30 | assert os.path.exists(fpkey_1_0)
31 | assert os.path.exists(cert_1_0)
32 | assert os.path.exists(chain_1_0)
33 | assert not os.path.exists(fpkey_1_1)
34 | assert not os.path.exists(cert_1_1)
35 | #
36 | md = env.a2md(["-vvv", "list", domain]).json['output'][0]
37 | assert domain == md["name"]
38 | #
39 | assert not os.path.exists(fpkey_1_0)
40 | assert os.path.exists(cert_1_0)
41 | assert os.path.exists(chain_1_0)
42 | assert os.path.exists(fpkey_1_1)
43 | assert os.path.exists(cert_1_1)
44 |
--------------------------------------------------------------------------------
/test/modules/md/test_741_setup_errors.py:
--------------------------------------------------------------------------------
1 | # test ACME error responses and their processing
2 | import os
3 |
4 | import pytest
5 |
6 | from .md_conf import MDConf
7 | from .md_env import MDTestEnv
8 |
9 |
10 | @pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(),
11 | reason="no ACME test server configured")
12 | class TestSetupErrors:
13 |
14 | @pytest.fixture(autouse=True, scope='class')
15 | def _class_scope(self, env, acme):
16 | env.APACHE_CONF_SRC = "data/test_auto"
17 | acme.start(config='default')
18 | env.check_acme()
19 | env.clear_store()
20 | MDConf(env).install()
21 | assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
22 |
23 | @pytest.fixture(autouse=True, scope='function')
24 | def _method_scope(self, env, request):
25 | env.clear_store()
26 | self.mcmd = os.path.join(env.test_dir, "../modules/md/http_challenge_foobar.py")
27 | self.test_domain = env.get_request_domain(request)
28 |
29 | def test_md_741_001(self, env):
30 | # setup an MD with a MDMessageCmd that make the http-01 challenge file invalid
31 | # before the ACME server is asked to retrieve it. This will result in
32 | # an "invalid" domain authorization.
33 | # The certificate sign-up will be attempted again after 4 seconds and
34 | # of course fail again.
35 | # Verify that the error counter for the staging job increments, so
36 | # that our retry logic goes into proper delayed backoff.
37 | domain = self.test_domain
38 | domains = [domain]
39 | conf = MDConf(env)
40 | conf.add("MDCAChallenges http-01")
41 | conf.add(f"MDMessageCmd {self.mcmd} {env.store_dir}")
42 | conf.add_md(domains)
43 | conf.add_vhost(domains)
44 | conf.install()
45 | assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
46 | md = env.await_error(domain, errors=2, timeout=10)
47 | assert md
48 | assert md['renewal']['errors'] > 0
49 | #
50 | env.httpd_error_log.ignore_recent(
51 | lognos = [
52 | "AH10056" # CA considers answer to challenge invalid
53 | ],
54 | matches = [
55 | r'.*The key authorization file from the server did not match this challenge.*',
56 | r'.*CA considers answer to challenge invalid.*'
57 | ]
58 | )
59 |
60 | # mess up the produced staging area before reload
61 | def test_md_741_002(self, env):
62 | domain = self.test_domain
63 | domains = [domain]
64 | conf = MDConf(env)
65 | conf.add_md(domains)
66 | conf.add_vhost(domains)
67 | conf.install()
68 | assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
69 | env.check_md(domains)
70 | assert env.await_completion([domain], restart=False)
71 | staged_md_path = env.store_staged_file(domain, 'md.json')
72 | with open(staged_md_path, 'w') as fd:
73 | fd.write('garbage\n')
74 | assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
75 | assert env.await_completion([domain])
76 | env.check_md_complete(domain)
77 | env.httpd_error_log.ignore_recent(
78 | lognos = [
79 | "AH10069" # failed to load JSON file
80 | ],
81 | matches = [
82 | r'.*failed to load JSON file.*',
83 | ]
84 | )
85 |
--------------------------------------------------------------------------------
/test/modules/md/test_800_must_staple.py:
--------------------------------------------------------------------------------
1 | # test mod_md must-staple support
2 | import pytest
3 |
4 | from .md_conf import MDConf
5 | from .md_cert_util import MDCertUtil
6 | from .md_env import MDTestEnv
7 |
8 |
9 | @pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(),
10 | reason="no ACME test server configured")
11 | class TestMustStaple:
12 | domain = None
13 |
14 | @pytest.fixture(autouse=True, scope='class')
15 | def _class_scope(self, env, acme):
16 | acme.start(config='default')
17 | env.check_acme()
18 | env.clear_store()
19 | MDConf(env).install()
20 | assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
21 |
22 | @pytest.fixture(autouse=True, scope='function')
23 | def _method_scope(self, env, request):
24 | self.domain = env.get_class_domain(self.__class__)
25 |
26 | def configure_httpd(self, env, domain, add_lines=""):
27 | conf = MDConf(env, admin="admin@" + domain)
28 | conf.add(add_lines)
29 | conf.add_md([domain])
30 | conf.add_vhost(domain)
31 | conf.install()
32 |
33 | # MD with default, e.g. not staple
34 | def test_md_800_001(self, env):
35 | self.configure_httpd(env, self.domain)
36 | assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
37 | assert env.await_completion([self.domain])
38 | env.check_md_complete(self.domain)
39 | cert1 = MDCertUtil(env.store_domain_file(self.domain, 'pubcert.pem'))
40 | assert not cert1.get_must_staple()
41 |
42 | # MD that should explicitly not staple
43 | def test_md_800_002(self, env):
44 | self.configure_httpd(env, self.domain, "MDMustStaple off")
45 | assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
46 | env.check_md_complete(self.domain)
47 | cert1 = MDCertUtil(env.store_domain_file(self.domain, 'pubcert.pem'))
48 | assert not cert1.get_must_staple()
49 | stat = env.get_ocsp_status(self.domain)
50 | assert 'ocsp' not in stat or stat['ocsp'] == "no response sent"
51 |
52 | # MD that must staple and toggle off again
53 | @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder")
54 | def test_md_800_003(self, env):
55 | self.configure_httpd(env, self.domain, "MDMustStaple on")
56 | assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
57 | assert env.await_completion([self.domain])
58 | env.check_md_complete(self.domain)
59 | cert1 = MDCertUtil(env.store_domain_file(self.domain, 'pubcert.pem'))
60 | assert cert1.get_must_staple()
61 | self.configure_httpd(env, self.domain, "MDMustStaple off")
62 | assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
63 | assert env.await_completion([self.domain])
64 | env.check_md_complete(self.domain)
65 | cert1 = MDCertUtil(env.store_domain_file(self.domain, 'pubcert.pem'))
66 | assert not cert1.get_must_staple()
67 |
68 | # MD that must staple
69 | @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder")
70 | @pytest.mark.skipif(MDTestEnv.get_ssl_module() != "mod_ssl", reason="only for mod_ssl")
71 | def test_md_800_004(self, env):
72 | # mod_ssl stapling is off, expect no stapling
73 | stat = env.get_ocsp_status(self.domain)
74 | assert stat['ocsp'] == "no response sent"
75 | # turn mod_ssl stapling on, expect an answer
76 | self.configure_httpd(env, self.domain, """
77 | LogLevel ssl:trace2
78 | SSLUseStapling On
79 | SSLStaplingCache shmcb:stapling_cache(128000)
80 | """)
81 | assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
82 | stat = env.get_ocsp_status(self.domain)
83 | assert stat['ocsp'] == "successful (0x0)"
84 | assert stat['verify'] == "0 (ok)"
85 |
--------------------------------------------------------------------------------
/test/modules/md/test_820_locks.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | import pytest
4 | from filelock import Timeout, FileLock
5 |
6 | from .md_cert_util import MDCertUtil
7 | from .md_conf import MDConf
8 | from .md_env import MDTestEnv
9 |
10 |
11 | @pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(),
12 | reason="no ACME test server configured")
13 | class TestLocks:
14 |
15 | @pytest.fixture(autouse=True, scope='class')
16 | def _class_scope(self, env, acme):
17 | env.APACHE_CONF_SRC = "data/test_auto"
18 | acme.start(config='default')
19 | env.check_acme()
20 | env.clear_store()
21 |
22 | @pytest.fixture(autouse=True, scope='function')
23 | def _method_scope(self, env, request):
24 | env.clear_store()
25 | self.test_domain = env.get_request_domain(request)
26 |
27 | def configure_httpd(self, env, domains, add_lines=""):
28 | conf = MDConf(env)
29 | conf.add(add_lines)
30 | conf.add_md(domains)
31 | conf.add_vhost(domains)
32 | conf.install()
33 |
34 | # normal renewal with store locks activated
35 | def test_md_820_001(self, env):
36 | domain = self.test_domain
37 | self.configure_httpd(env, [domain], add_lines=[
38 | "MDStoreLocks 1s"
39 | ])
40 | assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
41 | assert env.await_completion([domain])
42 |
43 | # renewal, with global lock held during restert
44 | @pytest.mark.skip("does not work in our CI")
45 | def test_md_820_002(self, env):
46 | domain = self.test_domain
47 | self.configure_httpd(env, [domain], add_lines=[
48 | "MDStoreLocks 1s"
49 | ])
50 | assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
51 | assert env.await_completion([domain])
52 | # we have a cert now, add a dns name to force renewal
53 | certa = MDCertUtil(env.store_domain_file(domain, 'pubcert.pem'))
54 | self.configure_httpd(env, [domain, f"x.{domain}"], add_lines=[
55 | "MDStoreLocks 1s"
56 | ])
57 | assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
58 | # await new cert, but do not restart, keeps the cert in staging
59 | assert env.await_completion([domain], restart=False)
60 | # obtain global lock and restart
61 | lockfile = os.path.join(env.store_dir, "store.lock")
62 | with FileLock(lockfile):
63 | assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
64 | # lock should have prevented staging from being activated,
65 | # meaning we will have the same cert
66 | certb = MDCertUtil(env.store_domain_file(domain, 'pubcert.pem'))
67 | assert certa.same_serial_as(certb)
68 | # now restart without lock
69 | assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
70 | certc = MDCertUtil(env.store_domain_file(domain, 'pubcert.pem'))
71 | assert not certa.same_serial_as(certc)
72 |
73 |
74 |
--------------------------------------------------------------------------------
/test/modules/md/test_910_cleanups.py:
--------------------------------------------------------------------------------
1 | # test mod_md cleanups and sanitation
2 |
3 | import os
4 |
5 | import pytest
6 |
7 | from .md_conf import MDConf
8 | from .md_env import MDTestEnv
9 |
10 |
11 | @pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(),
12 | reason="no ACME test server configured")
13 | class TestCleanups:
14 |
15 | @pytest.fixture(autouse=True, scope='class')
16 | def _class_scope(self, env, acme):
17 | env.APACHE_CONF_SRC = "data/test_auto"
18 | acme.start(config='default')
19 | env.check_acme()
20 | env.clear_store()
21 | MDConf(env).install()
22 | assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
23 |
24 | @pytest.fixture(autouse=True, scope='function')
25 | def _method_scope(self, env, request):
26 | env.clear_store()
27 | self.test_domain = env.get_request_domain(request)
28 |
29 | def teardown_method(self, method):
30 | print("teardown_method: %s" % method.__name__)
31 |
32 | def test_md_910_01(self, env):
33 | # generate a simple MD
34 | domain = self.test_domain
35 | domains = [domain]
36 | conf = MDConf(env)
37 | conf.add_drive_mode("manual")
38 | conf.add_md(domains)
39 | conf.add_vhost(domain)
40 | conf.install()
41 |
42 | # create valid/invalid challenges subdirs
43 | challenges_dir = env.store_challenges()
44 | dirs_before = ["aaa", "bbb", domain, "zzz"]
45 | for name in dirs_before:
46 | os.makedirs(os.path.join(challenges_dir, name))
47 |
48 | assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
49 | # the one we use is still there
50 | assert os.path.isdir(os.path.join(challenges_dir, domain))
51 | # and the others are gone
52 | missing_after = ["aaa", "bbb", "zzz"]
53 | for name in missing_after:
54 | assert not os.path.exists(os.path.join(challenges_dir, name))
55 |
--------------------------------------------------------------------------------
/test/pyhttpd/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/test/pyhttpd/__init__.py
--------------------------------------------------------------------------------
/test/pyhttpd/conf/httpd.conf.template:
--------------------------------------------------------------------------------
1 | ServerName localhost
2 | ServerRoot "${server_dir}"
3 |
4 | # not in 2.4.x
5 | #DefaultRuntimeDir logs
6 | PidFile "${server_dir}/logs/httpd.pid"
7 |
8 | Include "conf/modules.conf"
9 |
10 | DocumentRoot "${server_dir}/htdocs"
11 |
12 |
13 | LogFormat "{ \"request\": \"%r\", \"status\": %>s, \"bytes_resp_B\": %B, \"bytes_tx_O\": %O, \"bytes_rx_I\": %I, \"bytes_rx_tx_S\": %S, \"time_taken\": %D }" combined
14 | LogFormat "%h %l %u %t \"%r\" %>s %b" common
15 | CustomLog "logs/access_log" combined
16 |
17 |
18 |
19 | TypesConfig "${gen_dir}/apache/conf/mime.types"
20 |
21 | Listen ${http_port}
22 | Listen ${https_port}
23 |
24 |
25 | # provide some default
26 | SSLSessionCache "shmcb:ssl_gcache_data(32000)"
27 |
28 |
29 | # Insert our test specific configuration before the first vhost,
30 | # so that its vhosts can be the default one. This is relevant in
31 | # certain behaviours, such as protocol selection during SSL ALPN
32 | # negotiation.
33 | #
34 | Include "conf/test.conf"
35 |
36 | RequestReadTimeout header=10 body=10
37 |
38 |
39 | AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css
40 |
41 |
42 | AddOutputFilterByType BROTLI_COMPRESS text/html text/plain text/xml text/css
43 |
44 |
45 |
46 | ServerName ${http_tld}
47 | ServerAlias www.${http_tld}
48 |
49 | SSLEngine off
50 |
51 | DocumentRoot "${server_dir}/htdocs"
52 |
53 |
54 |
55 | Options Indexes FollowSymLinks
56 | AllowOverride None
57 | Require all granted
58 |
59 | AddHandler cgi-script .py
60 | AddHandler cgi-script .cgi
61 | Options +ExecCGI
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/test/pyhttpd/conf/stop.conf.template:
--------------------------------------------------------------------------------
1 | # a config safe to use for stopping the server
2 | # this allows us to stop the server even when+
3 | # the config in the file is borked (as test cases may try to do that)
4 | #
5 | ServerName localhost
6 | ServerRoot "${server_dir}"
7 |
8 | # not in 2.4.x
9 | #DefaultRuntimeDir logs
10 | PidFile "${server_dir}/logs/httpd.pid"
11 |
12 | Include "conf/modules.conf"
13 |
14 | DocumentRoot "${server_dir}/htdocs"
15 |
16 |
17 | LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\" %k" combined
18 | LogFormat "%h %l %u %t \"%r\" %>s %b" common
19 | CustomLog "logs/access_log" combined
20 |
21 |
22 |
23 | TypesConfig "${gen_dir}/apache/conf/mime.types"
24 |
25 | Listen ${http_port}
26 | Listen ${https_port}
27 |
28 |
29 | # provide some default
30 | SSLSessionCache "shmcb:ssl_gcache_data(32000)"
31 |
32 |
33 |
34 | ServerName ${http_tld}
35 | ServerAlias www.${http_tld}
36 |
37 | SSLEngine off
38 |
39 | DocumentRoot "${server_dir}/htdocs"
40 |
41 |
42 |
43 | Options Indexes FollowSymLinks
44 | AllowOverride None
45 | Require all granted
46 |
47 | AddHandler cgi-script .py
48 | AddHandler cgi-script .cgi
49 | Options +ExecCGI
50 |
51 |
--------------------------------------------------------------------------------
/test/pyhttpd/conf/test.conf:
--------------------------------------------------------------------------------
1 | # empty placeholder for test specific configurations
2 |
--------------------------------------------------------------------------------
/test/pyhttpd/config.ini.in:
--------------------------------------------------------------------------------
1 | [global]
2 | curl_bin = curl
3 | nghttp = nghttp
4 | h2load = h2load
5 |
6 | prefix = @prefix@
7 | exec_prefix = @exec_prefix@
8 | bindir = @bindir@
9 | sbindir = @sbindir@
10 | libdir = @libdir@
11 | libexecdir = @libexecdir@
12 |
13 | apr_bindir = @APR_BINDIR@
14 | apxs = @bindir@/apxs
15 | httpd = @HTTPD@
16 |
17 | [httpd]
18 | version = @HTTPD_VERSION@
19 | name = @progname@
20 | dso_modules = @DSO_MODULES@
21 | mpm_modules = @MPM_MODULES@
22 |
23 | [test]
24 | src_dir = @abs_top_srcdir@
25 | gen_dir = @abs_srcdir@/../gen
26 | http_port = 5002
27 | https_port = 5001
28 | proxy_port = 5003
29 | http_port2 = 5004
30 | ws_port = 5100
31 | http_tld = tests.httpd.apache.org
32 | test_dir = @abs_srcdir@
33 | test_src_dir = @abs_srcdir@
34 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/alive.json:
--------------------------------------------------------------------------------
1 | {
2 | "host" : "generic",
3 | "alive" : true
4 | }
5 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/cgi/echo.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys, cgi, os
3 |
4 | status = '200 Ok'
5 |
6 | content = ''
7 | for line in sys.stdin:
8 | content += line
9 |
10 | # Just echo what we get
11 | print("Status: 200")
12 | print(f"Request-Length: {len(content)}")
13 | print("Content-Type: application/data\n")
14 | sys.stdout.write(content)
15 |
16 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/cgi/echohd.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import cgi, os
3 | import cgitb; cgitb.enable()
4 |
5 | status = '200 Ok'
6 |
7 | form = cgi.FieldStorage()
8 | name = form.getvalue('name')
9 |
10 | if name:
11 | print("Status: 200")
12 | print("""\
13 | Content-Type: text/plain\n""")
14 | print("""%s: %s""" % (name, os.environ['HTTP_'+name]))
15 | else:
16 | print("Status: 400 Parameter Missing")
17 | print("""\
18 | Content-Type: text/html\n
19 |
20 | No name was specified
21 | """)
22 |
23 |
24 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/cgi/env.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import cgi, os
3 | import cgitb; cgitb.enable()
4 |
5 | status = '200 Ok'
6 |
7 | try:
8 | form = cgi.FieldStorage()
9 | input = form['name']
10 |
11 | # Test if the file was uploaded
12 | if input.value is not None:
13 | val = os.environ[input.value] if input.value in os.environ else ""
14 | print("Status: 200")
15 | print("""\
16 | Content-Type: text/plain\n""")
17 | print("{0}={1}".format(input.value, val))
18 |
19 | else:
20 | print("Status: 400 Parameter Missing")
21 | print("""\
22 | Content-Type: text/html\n
23 |
24 | No name was specified: %s
25 | """ % (count.value))
26 |
27 | except KeyError:
28 | print("Status: 200 Ok")
29 | print("""\
30 | Content-Type: text/html\n
31 |
32 | Echo
35 | """)
36 | pass
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/cgi/files/empty.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/test/pyhttpd/htdocs/cgi/files/empty.txt
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/cgi/hecho.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import cgi, os
3 | import cgitb; cgitb.enable()
4 |
5 | status = '200 Ok'
6 |
7 | try:
8 | form = cgi.FieldStorage()
9 |
10 | # A nested FieldStorage instance holds the file
11 | name = form['name'].value
12 | value = ''
13 |
14 | try:
15 | value = form['value'].value
16 | except KeyError:
17 | value = os.environ.get("HTTP_"+name, "unset")
18 |
19 | # Test if a value was given
20 | if name:
21 | print("Status: 200")
22 | print("%s: %s" % (name, value,))
23 | print ("""\
24 | Content-Type: text/plain\n""")
25 |
26 | else:
27 | print("Status: 400 Parameter Missing")
28 | print("""\
29 | Content-Type: text/html\n
30 |
31 | No name and value was specified: %s %s
32 | """ % (name, value))
33 |
34 | except KeyError:
35 | print("Status: 200 Ok")
36 | print("""\
37 | Content-Type: text/html\n
38 |
39 | Echo
43 | """)
44 | pass
45 |
46 |
47 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/cgi/hello.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import os
4 |
5 | print("Content-Type: application/json")
6 | print()
7 | print("{")
8 | print(" \"https\" : \"%s\"," % (os.getenv('HTTPS', '')))
9 | print(" \"host\" : \"%s\"," % (os.getenv('SERVER_NAME', '')))
10 | print(" \"protocol\" : \"%s\"," % (os.getenv('SERVER_PROTOCOL', '')))
11 | print(" \"ssl_protocol\" : \"%s\"," % (os.getenv('SSL_PROTOCOL', '')))
12 | print(" \"h2\" : \"%s\"," % (os.getenv('HTTP2', '')))
13 | print(" \"h2push\" : \"%s\"" % (os.getenv('H2PUSH', '')))
14 | print("}")
15 |
16 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/cgi/mnot164.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import cgi
4 | import cgitb; cgitb.enable()
5 | import os
6 | import sys
7 |
8 | try:
9 | form = cgi.FieldStorage()
10 | count = form['count'].value
11 | text = form['text'].value
12 | except KeyError:
13 | text="a"
14 | count=77784
15 |
16 |
17 | print("Status: 200 OK")
18 | print("Content-Type: text/html")
19 | print()
20 | sys.stdout.write(text*int(count))
21 |
22 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/cgi/necho.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import cgi, os
3 | import time
4 | import cgitb; cgitb.enable()
5 |
6 | status = '200 Ok'
7 |
8 | try:
9 | form = cgi.FieldStorage()
10 | count = form['count']
11 | text = form['text']
12 |
13 | waitsec = float(form['wait1'].value) if 'wait1' in form else 0.0
14 | if waitsec > 0:
15 | time.sleep(waitsec)
16 |
17 | if int(count.value):
18 | print("Status: 200")
19 | print("""\
20 | Content-Type: text/plain\n""")
21 |
22 | waitsec = float(form['wait2'].value) if 'wait2' in form else 0.0
23 | if waitsec > 0:
24 | time.sleep(waitsec)
25 |
26 | i = 0;
27 | for i in range(0, int(count.value)):
28 | print("%s" % (text.value))
29 |
30 | waitsec = float(form['wait3'].value) if 'wait3' in form else 0.0
31 | if waitsec > 0:
32 | time.sleep(waitsec)
33 |
34 | else:
35 | print("Status: 400 Parameter Missing")
36 | print("""\
37 | Content-Type: text/html\n
38 |
39 | No count was specified: %s
40 | """ % (count.value))
41 |
42 | except KeyError:
43 | print("Status: 200 Ok")
44 | print("""\
45 | Content-Type: text/html\n
46 |
47 | Echo
51 | """)
52 | pass
53 |
54 |
55 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/cgi/upload.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import cgi, os
3 | import cgitb
4 | cgitb.enable()
5 |
6 | status = '200 Ok'
7 |
8 | try: # Windows needs stdio set for binary mode.
9 | import msvcrt
10 | msvcrt.setmode (0, os.O_BINARY) # stdin = 0
11 | msvcrt.setmode (1, os.O_BINARY) # stdout = 1
12 | except ImportError:
13 | pass
14 |
15 | form = cgi.FieldStorage()
16 |
17 | # Test if the file was uploaded
18 | if 'file' in form:
19 | fileitem = form['file']
20 | # strip leading path from file name to avoid directory traversal attacks
21 | fn = os.path.basename(fileitem.filename)
22 | f = open(('%s/files/%s' % (os.environ["DOCUMENT_ROOT"], fn)), 'wb');
23 | f.write(fileitem.file.read())
24 | f.close()
25 | message = "The file %s was uploaded successfully" % (fn)
26 | print("Status: 201 Created")
27 | print("Content-Type: text/html")
28 | print("Location: %s://%s/files/%s" % (os.environ["REQUEST_SCHEME"], os.environ["HTTP_HOST"], fn))
29 | print("")
30 | print("%s
" % (message))
31 |
32 | elif 'remove' in form:
33 | remove = form['remove'].value
34 | try:
35 | fn = os.path.basename(remove)
36 | os.remove('./files/' + fn)
37 | message = 'The file "' + fn + '" was removed successfully'
38 | except OSError as e:
39 | message = 'Error removing ' + fn + ': ' + e.strerror
40 | status = '404 File Not Found'
41 | print("Status: %s" % (status))
42 | print("""
43 | Content-Type: text/html
44 |
45 |
46 | %s
47 | """ % (message))
48 |
49 | else:
50 | message = '''\
51 | Upload File
54 | '''
55 | print("Status: %s" % (status))
56 | print("""\
57 | Content-Type: text/html
58 |
59 |
60 | %s
61 | """ % (message))
62 |
63 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/forbidden.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | 403 - Forbidden
4 |
5 |
6 | 403 - Forbidden
7 |
8 | An example of an error document.
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | mod_h2 test site generic
4 |
5 |
6 | mod_h2 test site generic
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/noh2/alive.json:
--------------------------------------------------------------------------------
1 | {
2 | "host" : "noh2",
3 | "alive" : true
4 | }
5 |
6 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/noh2/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | mod_h2 test site noh2
4 |
5 |
6 | mod_h2 test site noh2
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/001.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | HTML/2.0 Test File: 001
5 |
6 |
7 | HTML/2.0 Test File: 001
8 | This file only contains a simple HTML structure with plain text.
9 |
10 |
11 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/002.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/test/pyhttpd/htdocs/test1/002.jpg
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/003.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | HTML/2.0 Test File: 003
5 |
6 |
7 | HTML/2.0 Test File: 003
8 | This is a text HTML file with a big image:
9 | 
10 |
11 |
12 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/003/003_img.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/test/pyhttpd/htdocs/test1/003/003_img.jpg
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/004/gophertiles.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/test/pyhttpd/htdocs/test1/004/gophertiles.jpg
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/006.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | HTML/2.0 Test File: 006
5 |
6 |
7 |
8 |
9 | HTML/2.0 Test File: 006
10 | This page contains:
11 |
12 | - HTML
13 |
- CSS
14 |
- JavaScript
15 |
16 |
17 |
18 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/006/006.css:
--------------------------------------------------------------------------------
1 | @CHARSET "ISO-8859-1";
2 | body{
3 | background:HoneyDew;
4 | }
5 | p{
6 | color:#0000FF;
7 | text-align:left;
8 | }
9 |
10 | h1{
11 | color:#FF0000;
12 | text-align:center;
13 | }
14 |
15 | .listTitle{
16 | font-size:large;
17 | }
18 |
19 | .listElements{
20 | color:#3366FF
21 | }
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/006/006.js:
--------------------------------------------------------------------------------
1 | /**
2 | * JavaScript Functions File
3 | */
4 | function returnDate()
5 | {
6 | var currentDate;
7 | currentDate=new Date();
8 | var dateString=(currentDate.getMonth()+1)+'/'+currentDate.getDate()+'/'+currentDate.getFullYear();
9 | return dateString;
10 | }
11 |
12 | function returnHour()
13 | {
14 | var currentDate;
15 | currentDate=new Date();
16 | var hourString=currentDate.getHours()+':'+currentDate.getMinutes()+':'+currentDate.getSeconds();
17 | return hourString;
18 | }
19 |
20 | function javaScriptMessage(){
21 | return 'This section is generated under JavaScript:
';
22 | }
23 |
24 | function mainJavascript(){
25 | document.write(javaScriptMessage())
26 | document.write('');
27 | document.write('- Current date (dd/mm/yyyy): ' + returnDate());
28 | document.write('
');
29 | document.write(' - Current time (hh:mm:ss): '+returnHour());
30 | document.write('
');
31 | }
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/006/header.html:
--------------------------------------------------------------------------------
1 | My Header Title
2 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/007.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | HTML/2.0 Test File: 007
6 |
7 |
8 | HTML/2.0 Test File: 007
9 | This page is used to send data from the client to the server:
10 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/007/007.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | import cgi, sys
4 | import cgitb; cgitb.enable()
5 |
6 | print "Content-Type: text/html;charset=UTF-8"
7 | print
8 |
9 | print """\
10 |
11 | HTML/2.0 Test File: 007 (received data)
12 | HTML/2.0 Test File: 007
"""
13 |
14 | # alternative output: parsed form params <-> plain POST body
15 | parseContent = True # <-> False
16 |
17 | if parseContent:
18 | print 'Data processed:
'
19 | form = cgi.FieldStorage()
20 | for name in form:
21 | print '- ', name, ': ', form[name].value, '
'
22 | print '
'
23 | else:
24 | print 'POST data output:
'
25 | data = sys.stdin.read()
26 | print data
27 | print '
'
28 |
29 | print ''
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/009.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | import cgi, sys, time
4 | import cgitb; cgitb.enable()
5 |
6 | print "Content-Type: text/html;charset=UTF-8"
7 | print
8 |
9 | print """\
10 |
11 | HTML/2.0 Test File: 009 (server time)
12 | HTML/2.0 Test File: 009
13 | 60 seconds of server time, one by one.
"""
14 |
15 | for i in range(60):
16 | s = time.strftime("%Y-%m-%d %H:%M:%S")
17 | print "", s, "
"
18 | sys.stdout.flush()
19 | time.sleep(1)
20 |
21 | print "done.
"
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/alive.json:
--------------------------------------------------------------------------------
1 | {
2 | "host" : "test1",
3 | "alive" : true
4 | }
5 |
6 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/apache.org-files/ant.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/test/pyhttpd/htdocs/test1/apache.org-files/ant.jpg
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/apache.org-files/asf_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/test/pyhttpd/htdocs/test1/apache.org-files/asf_logo.png
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/apache.org-files/async-ads.js.br:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/test/pyhttpd/htdocs/test1/apache.org-files/async-ads.js.br
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/apache.org-files/cse.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/test/pyhttpd/htdocs/test1/apache.org-files/cse.js
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/apache.org-files/css.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/test/pyhttpd/htdocs/test1/apache.org-files/css.css
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/apache.org-files/jsapi.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/test/pyhttpd/htdocs/test1/apache.org-files/jsapi.js
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/apache.org-files/min.css.br:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/test/pyhttpd/htdocs/test1/apache.org-files/min.css.br
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/apache.org-files/mrunit.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/test/pyhttpd/htdocs/test1/apache.org-files/mrunit.jpg
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/apache.org-files/search_box_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/test/pyhttpd/htdocs/test1/apache.org-files/search_box_icon.png
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/apache.org-files/small-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/test/pyhttpd/htdocs/test1/apache.org-files/small-logo.png
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/apache.org-files/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top:60px;
3 | background-color: #fff;
4 | }
5 |
6 | h1, h2, h3, h4, h5, h6 {
7 | text-transform: uppercase;
8 | }
9 |
10 | .navbar-default .navbar-toggle {
11 | border: none;
12 | }
13 |
14 | .navbar-toggle {
15 | margin-top: 0px;
16 | margin-bottom: 0px;
17 | }
18 |
19 | section {
20 | padding-bottom: 25px;
21 | }
22 |
23 | ul.white > li > a,
24 | a.white {
25 | color: #ddd;
26 | }
27 |
28 | ul.white > li > a:hover,
29 | a.white:hover {
30 | color: #fff;
31 | }
32 |
33 | .white {
34 | color: #fff;
35 | }
36 |
37 | .bg-gray {
38 | background: #eee;
39 | }
40 |
41 | .letter-header {
42 | display: block;
43 | background-color: #f3f3f3;
44 | color: #303284;
45 | text-align: center;
46 | }
47 |
48 | .list-unstyled {
49 | list-style: none;
50 | }
51 |
52 | .no-top-margin{
53 | margin-top: 0px;
54 | }
55 |
56 | .no-btm-margin {
57 | margin-bottom: 0px;
58 | }
59 |
60 |
61 | /* Custom Left Tabs */
62 |
63 | .tabs-left > .nav-tabs {
64 | border-bottom: 0;
65 | }
66 |
67 | .tabs-left > .nav-tabs > li,
68 | .tabs-right > .nav-tabs > li {
69 | float: none;
70 | }
71 |
72 | .tabs-left > .nav-tabs > li > a,
73 | .tabs-right > .nav-tabs > li > a {
74 | min-width: 74px;
75 | margin-right: 0;
76 | margin-bottom: 3px;
77 | border: none;
78 | }
79 |
80 | .tabs-left > .nav-tabs > li > a:hover:after {
81 | content: "";
82 | position: absolute;
83 | right: -24px;
84 | top: 50%;
85 | width: 0;
86 | height: 0;
87 | border-top: 25px solid transparent;
88 | border-bottom: 25px solid transparent;
89 | border-left: 25px solid #eee;
90 | margin-top: -25px;
91 | }
92 |
93 | .tabs-left > .nav-tabs > .active > a:after,
94 | .tabs-left > .nav-tabs > .active > a:hover:after {
95 | content: "";
96 | position: absolute;
97 | right: -24px;
98 | top: 50%;
99 | width: 0;
100 | height: 0;
101 | border-top: 25px solid transparent;
102 | border-bottom: 25px solid transparent;
103 | border-left: 25px solid #303284;
104 | margin-top: -25px;
105 | }
106 |
107 |
108 | .tabs-left > .nav-tabs > li > a {
109 | margin-right: -1px;
110 | width: 90%;
111 | }
112 |
113 | .tabs-left > .nav-tabs > li > a:hover,
114 | .tabs-left > .nav-tabs > li > a:focus {
115 | border: none;
116 | }
117 |
118 | /* Media Adjustments */
119 | @media (max-width: 992px){
120 | .navbar-nav > li > a {
121 | padding-left: 5px;
122 | }
123 | }
124 |
125 | /* Prevent anchor links from displaying under the navbar */
126 | :target:before {
127 | content: "";
128 | display: block;
129 | height: 30px;
130 | margin-top: -30px;
131 | }
132 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/apache.org-files/synapse.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/test/pyhttpd/htdocs/test1/apache.org-files/synapse.jpg
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test1/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | mod_h2 test site
4 |
5 |
6 | mod_h2 test site
7 |
8 | served directly
9 |
20 | mod_proxyied
21 |
32 | mod_rewritten
33 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test2/006/006.css:
--------------------------------------------------------------------------------
1 | @CHARSET "ISO-8859-1";
2 | body{
3 | background:HoneyDew;
4 | }
5 | p{
6 | color:#0000FF;
7 | text-align:left;
8 | }
9 |
10 | h1{
11 | color:#FF0000;
12 | text-align:center;
13 | }
14 |
15 | .listTitle{
16 | font-size:large;
17 | }
18 |
19 | .listElements{
20 | color:#3366FF
21 | }
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test2/10%abnormal.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/test/pyhttpd/htdocs/test2/10%abnormal.txt
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test2/alive.json:
--------------------------------------------------------------------------------
1 | {
2 | "host" : "test2",
3 | "alive" : true
4 | }
5 |
--------------------------------------------------------------------------------
/test/pyhttpd/htdocs/test2/x%2f.test:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icing/mod_md/9071b12a11c824f73fc54a29c81be885e9580745/test/pyhttpd/htdocs/test2/x%2f.test
--------------------------------------------------------------------------------
/test/pyhttpd/mod_aptest/mod_aptest.c:
--------------------------------------------------------------------------------
1 | /* Licensed to the Apache Software Foundation (ASF) under one or more
2 | * contributor license agreements. See the NOTICE file distributed with
3 | * this work for additional information regarding copyright ownership.
4 | * The ASF licenses this file to You under the Apache License, Version 2.0
5 | * (the "License"); you may not use this file except in compliance with
6 | * the License. You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 |
23 | #include
24 | #include
25 | #include
26 | #include
27 |
28 | static void aptest_hooks(apr_pool_t *pool);
29 |
30 | AP_DECLARE_MODULE(aptest) = {
31 | STANDARD20_MODULE_STUFF,
32 | NULL, /* func to create per dir config */
33 | NULL, /* func to merge per dir config */
34 | NULL, /* func to create per server config */
35 | NULL, /* func to merge per server config */
36 | NULL, /* command handlers */
37 | aptest_hooks,
38 | #if defined(AP_MODULE_FLAG_NONE)
39 | AP_MODULE_FLAG_ALWAYS_MERGE
40 | #endif
41 | };
42 |
43 |
44 | static int aptest_post_read_request(request_rec *r)
45 | {
46 | const char *test_name = apr_table_get(r->headers_in, "AP-Test-Name");
47 | if (test_name) {
48 | ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "test[%s]: %s",
49 | test_name, r->the_request);
50 | }
51 | return DECLINED;
52 | }
53 |
54 | /* Install this module into the apache2 infrastructure.
55 | */
56 | static void aptest_hooks(apr_pool_t *pool)
57 | {
58 | ap_log_perror(APLOG_MARK, APLOG_TRACE1, 0, pool,
59 | "installing hooks and handlers");
60 |
61 | /* test case monitoring */
62 | ap_hook_post_read_request(aptest_post_read_request, NULL,
63 | NULL, APR_HOOK_MIDDLE);
64 |
65 | }
66 |
67 |
--------------------------------------------------------------------------------
/test/pyhttpd/result.py:
--------------------------------------------------------------------------------
1 | import json
2 | from datetime import timedelta
3 | from typing import Optional, Dict, List
4 |
5 |
6 | class ExecResult:
7 |
8 | def __init__(self, args: List[str], exit_code: int,
9 | stdout: bytes, stderr: bytes = None,
10 | stdout_as_list: List[bytes] = None,
11 | duration: timedelta = None):
12 | self._args = args
13 | self._exit_code = exit_code
14 | self._stdout = stdout if stdout is not None else b''
15 | self._stderr = stderr if stderr is not None else b''
16 | self._duration = duration if duration is not None else timedelta()
17 | self._response = None
18 | self._results = {}
19 | self._assets = []
20 | # noinspection PyBroadException
21 | try:
22 | if stdout_as_list is None:
23 | out = self._stdout.decode()
24 | else:
25 | out = "[" + ','.join(stdout_as_list) + "]"
26 | self._json_out = json.loads(out)
27 | except:
28 | self._json_out = None
29 |
30 | def __repr__(self):
31 | out = [
32 | f"ExecResult[code={self.exit_code}, args={self._args}\n",
33 | "----stdout---------------------------------------\n",
34 | self._stdout.decode(),
35 | "----stderr---------------------------------------\n",
36 | self._stderr.decode()
37 | ]
38 | return ''.join(out)
39 |
40 | @property
41 | def exit_code(self) -> int:
42 | return self._exit_code
43 |
44 | @property
45 | def args(self) -> List[str]:
46 | return self._args
47 |
48 | @property
49 | def outraw(self) -> bytes:
50 | return self._stdout
51 |
52 | @property
53 | def stdout(self) -> str:
54 | return self._stdout.decode()
55 |
56 | @property
57 | def json(self) -> Optional[Dict]:
58 | """Output as JSON dictionary or None if not parseable."""
59 | return self._json_out
60 |
61 | @property
62 | def stderr(self) -> str:
63 | return self._stderr.decode()
64 |
65 | @property
66 | def duration(self) -> timedelta:
67 | return self._duration
68 |
69 | @property
70 | def response(self) -> Optional[Dict]:
71 | return self._response
72 |
73 | @property
74 | def results(self) -> Dict:
75 | return self._results
76 |
77 | @property
78 | def assets(self) -> List:
79 | return self._assets
80 |
81 | def add_response(self, resp: Dict):
82 | if self._response:
83 | resp['previous'] = self._response
84 | self._response = resp
85 |
86 | def add_results(self, results: Dict):
87 | self._results.update(results)
88 | if 'response' in results:
89 | self.add_response(results['response'])
90 |
91 | def add_assets(self, assets: List):
92 | self._assets.extend(assets)
93 |
--------------------------------------------------------------------------------
/test/requirements.txt:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Copyright 2025 Stefan Eissing (https://dev-icing.de)
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | #
16 | pytest
17 | cryptography
18 | filelock
19 | python-multipart
20 | psutil
21 |
--------------------------------------------------------------------------------
/test/unit/main.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 | #include "test_common.h"
16 |
17 | #include
18 |
19 | static Suite *main_test_suite(void)
20 | {
21 | Suite *suite = suite_create("main");
22 |
23 | suite_add_tcase(suite, md_json_test_case());
24 | suite_add_tcase(suite, md_util_test_case());
25 |
26 | return suite;
27 | }
28 |
29 | int main(int argc, const char * const argv[])
30 | {
31 | SRunner *runner;
32 | int failed;
33 |
34 | /* Initialize APR and create our test runner. */
35 | apr_app_initialize(&argc, &argv, NULL);
36 | runner = srunner_create(main_test_suite());
37 |
38 | /* Log TAP to stdout. */
39 | srunner_set_tap(runner, "-");
40 |
41 | /* Run the tests and collect failures. */
42 | srunner_run_all(runner, CK_SILENT /* output only TAP */);
43 | failed = srunner_ntests_failed(runner);
44 |
45 | /* Clean up. */
46 | srunner_free(runner);
47 | apr_terminate();
48 |
49 | return failed ? 1 : 0;
50 | }
51 |
--------------------------------------------------------------------------------
/test/unit/test_common.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 | /*
16 | * Common headers and declarations needed by most/all test source files.
17 | */
18 |
19 | #include /* for pid_t on Windows, needed by Check */
20 | #include
21 |
22 | /*
23 | * Compatibility ck_assert macros for Check < 0.11.
24 | */
25 | #if (CHECK_MAJOR_VERSION == 0) && (CHECK_MINOR_VERSION < 11)
26 | # include /* fabs() */
27 | # include /* memcmp() */
28 |
29 | # define ck_assert_double_eq_tol(a, b, tol) \
30 | ck_assert(fabs((a) - (b)) <= (tol))
31 | # define ck_assert_mem_eq(a, b, len) \
32 | ck_assert(!memcmp((a), (b), (len)))
33 | # define ck_assert_ptr_nonnull(p) \
34 | ck_assert((p) != NULL)
35 | #endif
36 |
37 | /*
38 | * A list of Check test case declarations, usually one per source file. Add your
39 | * test case here when adding a new source file, then add it to the
40 | * main_test_suite() in main.c.
41 | */
42 |
43 | TCase *md_json_test_case(void);
44 | TCase *md_util_test_case(void);
45 |
--------------------------------------------------------------------------------
/test/unit/test_md_util.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 | #include
16 |
17 | #include "test_common.h"
18 | #include "md_util.h"
19 |
20 | /*
21 | * Helpers
22 | */
23 |
24 | /*
25 | * Test Fixture -- runs once per test
26 | */
27 |
28 | static apr_pool_t *g_pool;
29 |
30 | static void md_util_setup(void)
31 | {
32 | if (apr_pool_create(&g_pool, NULL) != APR_SUCCESS) {
33 | exit(1);
34 | }
35 | }
36 |
37 | static void md_util_teardown(void)
38 | {
39 | apr_pool_destroy(g_pool);
40 | }
41 |
42 | static void base64_roundtrip(const char *buf_in, size_t buf_len)
43 | {
44 | const char *buf64;
45 | md_data_t buffer;
46 |
47 | buffer.data = buf_in;
48 | buffer.len = buf_len;
49 |
50 | buf64 = md_util_base64url_encode(&buffer, g_pool);
51 | ck_assert(buf64);
52 | md_util_base64url_decode(&buffer, buf64, g_pool);
53 |
54 | ck_assert_int_eq(buf_len, buffer.len);
55 | ck_assert_mem_eq(buf_in, buffer.data, buffer.len);
56 | }
57 |
58 | /*
59 | * Tests
60 | */
61 | START_TEST(base64_md_util_roundtrip)
62 | {
63 | base64_roundtrip("1", 1);
64 | base64_roundtrip("12", 2);
65 | base64_roundtrip("123", 3);
66 | base64_roundtrip("1234", 4);
67 | base64_roundtrip("12345", 5);
68 | base64_roundtrip("123456", 6);
69 | base64_roundtrip("1234567", 7);
70 | base64_roundtrip("12345678", 8);
71 | base64_roundtrip("123456789", 9);
72 | }
73 | END_TEST
74 |
75 | static void largetrip(int step)
76 | {
77 | char buffer[256];
78 | int i, start;
79 |
80 | for (start = 0; start < 256; ++start) {
81 | for (i = 0; i < 256; ++i) {
82 | buffer[(start+(i*step)) % 256] = (char)i;
83 | }
84 | base64_roundtrip(buffer, 256);
85 | }
86 | }
87 |
88 | START_TEST(base64_md_util_largetrip)
89 | {
90 | largetrip(1);
91 | largetrip(3);
92 | largetrip(5);
93 | largetrip(17);
94 | largetrip(31);
95 | largetrip(53);
96 | largetrip(101);
97 | largetrip(167);
98 | largetrip(223);
99 | }
100 | END_TEST
101 |
102 | TCase *md_util_test_case(void)
103 | {
104 | TCase *testcase = tcase_create("md_util");
105 |
106 | tcase_add_checked_fixture(testcase, md_util_setup, md_util_teardown);
107 |
108 | tcase_add_test(testcase, base64_md_util_roundtrip);
109 | tcase_add_test(testcase, base64_md_util_largetrip);
110 |
111 | return testcase;
112 | }
113 |
--------------------------------------------------------------------------------