Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Report bugs to deSEC.io API #2180

Open
sotux opened this issue Mar 20, 2019 · 14 comments
Open

Report bugs to deSEC.io API #2180

sotux opened this issue Mar 20, 2019 · 14 comments
Assignees
Labels
3rd party api report bugs to dns api, deploy hooks and notification hooks

Comments

@sotux
Copy link
Contributor

sotux commented Mar 20, 2019

This is the place to report bugs in the deSEC.io DNS API.

If you experience a bug, please report it in this issue.

@Neilpang Neilpang added the 3rd party api report bugs to dns api, deploy hooks and notification hooks label Mar 21, 2019
@KreMic
Copy link

KreMic commented Jul 25, 2019

Minor typos in dns_desec.sh (3bb97b8 on 21 Mar)

In lines 28 and 95:
"You don't specify DEDYN_TOKEN and DEDYN_NAME yet."
should better be
"You did not specify DEDYN_TOKEN and DEDYN_NAME yet."
and in lines 29 and 96
"Please create you key and try again."
should better be
"Please create your key and try again."
sincere greetings KreMic
and thank you for the effort you take to make things a little more secure!

@sotux
Copy link
Contributor Author

sotux commented Aug 5, 2019

@KreMic Thank you. I've made a PR to fix the typo.

@devudopw
Copy link

Hello
Using v2.8.5, debug mode shows error when add text record.

GET
url='https://desec.io/api/v1/domains/'
timeout=
_CURL='curl -L --silent --dump-header /acme.sh/http.header  -g '
ret='0'
http response code 200
_sub_domain='_acme-challenge'
_domain='xxx.com'
Getting txt records
GET
url='https://desec.io/api/v1/domains/xxx.com/rrsets/_acme-challenge/TXT/'
timeout=
_CURL='curl -L --silent --dump-header /acme.sh/http.header  -g '
ret='0'
http response code 404
txtvalues='"\"lviE8q8rmFnnWI13jv_gwmp9HMxVF-eMGxL8JY4kSP0\""'
Adding record
PUT
_post_url='https://desec.io/api/v1/domains/xxx.com/rrsets/'
_CURL='curl -L --silent --dump-header /acme.sh/http.header  -g '
_ret='0'
http response code 400
Add txt record error.

@sotux
Copy link
Contributor Author

sotux commented May 9, 2020

Hi @devudopw, could you use --debug 2 to provide some more detail output?

@devudopw
Copy link

Hi @devudopw, could you use --debug 2 to provide some more detail output?

Hello, I just resolved myself, later I will create PR for this.

@vikanezrimaya
Copy link

Oops! Turns out there's a place to report 3rd party API bugs. Well, there's mine: #2925

TTL is hard-coded to 60, which makes this program unusable when passed a non-dedyn.io domain. The workaround is to apply for a lower TTL, but I'm not sure how eagerly does desec.io approve these requests.

@devudopw
Copy link

devudopw commented Jul 5, 2020

Oops! Turns out there's a place to report 3rd party API bugs. Well, there's mine: #2925

TTL is hard-coded to 60, which makes this program unusable when passed a non-dedyn.io domain. The workaround is to apply for a lower TTL, but I'm not sure how eagerly does desec.io approve these requests.

Yes, the issue is about TTL, change from 60 to 3600 is working now
more from desec.io doc

If you would like to use lower TTL values, you can apply for an exception by contacting support. We reserve the right to reject applications at our discretion.

@Kojo-Agyemang
Copy link

Kojo-Agyemang commented Sep 20, 2020

I can't seem to get DNS API/DESEC working when running it from inside a docker container. This is the command I use
docker run --rm -it -v "$(pwd)/acme.sh":/acme.sh --net=host neilpang/acme.sh --issue --staging --dns dns_desec -d condolencesghana.com --debug 2

The debug info says there is a libcurl error with code 92 when it tries to connect to "https://desec.io/api/v1/domains". Upon looking at the dns_desec.sh file, I believe the error originates from line 159. Is it a question of outdated docker image?

The below is an excerpt debug information from the console.

[Sun Sep 20 15:35:32 UTC 2020] d='condolencesghana.com'
[Sun Sep 20 15:35:32 UTC 2020] _d_alias
[Sun Sep 20 15:35:32 UTC 2020] txtdomain='_acme-challenge.condolencesghana.com'
[Sun Sep 20 15:35:32 UTC 2020] txt='qSrbJ1iZfTsFz5Df5L9pfzOnTIMCMSoUDWt0ombtmV0'
[Sun Sep 20 15:35:32 UTC 2020] d_api='/root/.acme.sh/dnsapi/dns_desec.sh'
[Sun Sep 20 15:35:32 UTC 2020] dns_entry='condolencesghana.com,_acme-challenge.condolencesghana.com,,dns_desec,qSrbJ1iZfTsFz5Df5L9pfzOnTIMCMSoUDWt0ombtmV0,/root/.acme.sh/dnsapi/dns_desec.sh'
[Sun Sep 20 15:35:32 UTC 2020] Found domain api file: /root/.acme.sh/dnsapi/dns_desec.sh
[Sun Sep 20 15:35:32 UTC 2020] Adding txt value: qSrbJ1iZfTsFz5Df5L9pfzOnTIMCMSoUDWt0ombtmV0 for domain: acme-challenge.condolencesghana.com
[Sun Sep 20 15:35:32 UTC 2020] Using desec.io api
[Sun Sep 20 15:35:32 UTC 2020] fulldomain='acme-challenge.condolencesghana.com'
[Sun Sep 20 15:35:32 UTC 2020] txtvalue='qSrbJ1iZfTsFz5Df5L9pfzOnTIMCMSoUDWt0ombtmV0'
[Sun Sep 20 15:35:32 UTC 2020] First detect the root zone
[Sun Sep 20 15:35:32 UTC 2020] h='condolencesghana.com'
[Sun Sep 20 15:35:32 UTC 2020] GET
[Sun Sep 20 15:35:32 UTC 2020] url='https://desec.io/api/v1/domains/'
[Sun Sep 20 15:35:32 UTC 2020] timeout=
[Sun Sep 20 15:35:32 UTC 2020] Http already initialized.
[Sun Sep 20 15:35:32 UTC 2020] CURL='curl -L --silent --dump-header /acme.sh/http.header --trace-ascii /tmp/tmp.N1WtUoiNxk -g '
[Sun Sep 20 15:35:32 UTC 2020] Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: 92
[Sun Sep 20 15:35:32 UTC 2020] Here is the curl dump log:
[Sun Sep 20 15:35:32 UTC 2020] == Info: Trying 88.99.64.5:443...
== Info: Connected to desec.io (88.99.64.5) port 443 (#0)
== Info: ALPN, offering h2
== Info: ALPN, offering http/1.1
== Info: successfully set certificate verify locations:
== Info: CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: none
=> Send SSL data, 5 bytes (0x5)
0000: .....
== Info: TLSv1.3 (OUT), TLS handshake, Client hello (1):
=> Send SSL data, 512 bytes (0x200)
0000: .......qe..G..)..r.S../&S5.v.R.8..m.8p 1U...)d..*....:...TK...{(
0040: ..$wu"u.>.......,.0.........+./...$.(.k.#.'.g.....9.....3.....=.
0080: <.5./.....u.........desec.io........................3t.........h
00c0: 2.http/1.1.........1.....0......................................
0100: ...........+............-.....3.&.$... ......
.X4......G.......}
0140: ...=..3.........................................................
0180: ................................................................
01c0: ................................................................
<= Recv SSL data, 5 bytes (0x5)
0000: ....z
== Info: TLSv1.3 (IN), TLS handshake, Server hello (2):
<= Recv SSL data, 122 bytes (0x7a)
0000: ...v...-.........z..X.o..a......!...s. 1U...)d..*....:...TK...{(
0040: ..$wu"u......+.....3.$... ......D.......y.:0........it....
<= Recv SSL data, 5 bytes (0x5)
0000: .....
<= Recv SSL data, 5 bytes (0x5)
0000: ....$
<= Recv SSL data, 1 bytes (0x1)
0000: .
== Info: TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
<= Recv SSL data, 19 bytes (0x13)
0000: .................h2
<= Recv SSL data, 5 bytes (0x5)
0000: ....!
<= Recv SSL data, 1 bytes (0x1)
0000: .
== Info: TLSv1.3 (IN), TLS handshake, Certificate (11):
<= Recv SSL data, 2576 bytes (0xa10)
0000: ..........h0..d0..L........ZV.
.$q...u.....-0....H........0J1.0
0040: ...U....US1.0...U....Let's Encrypt1#0!..U....Let's Encrypt Autho
0080: rity X30...200807151340Z..201105151340Z0.1.0...U....desec.io0.."
00c0: 0...
.H.............0..........= ..V.....<)A....&.Tj....bD.2.W.
0100: ..E...6.'[email protected].}S.."...
........X.&....C[..Y.D&-..V;im
0140: 6.rN..4.......P..J[...[q.QW=.. ^.. ..w..ng.Yna...40.........a.
0180: c.......V..}...Br...f....'L.. Z..Q....+.-v..K....+....2UO...M...
01c0: P...A...e#...Y.#..O..(..9.j.........y0..u0...U...........0...U. 0200: %..0...+.........+.......0...U.......0.0...U......c..y..r.....I. 0240: j.y.O.0...U.#..0....Jjc.}....9..Ee.....0o..+........c0a0...+.... 0280: .0.."http://ocsp.int-x3.letsencrypt.org0/..+.....0..#http://cert 02c0: .int-x3.letsencrypt.org/0/..U...(0&..desec.io..get.desec.io..www 0300: .desec.io0L..U. .E0C0...g.....07..+..........0(0&..+.........htt 0340: p://cps.letsencrypt.org0.....+.....y............v.^.s..V...6H}.I 0380: .2z.........u..qEX...s.........G0E. .;....O.[[email protected]. 03c0: .<}..!..R...[.D..R........\-g.[0.%M...v......... N.f.+..% gk.. 0400: p..IS-...^...s.........G0E.!.....O../#J...f......... ..j.wh... * 0440: .v...)......h,..!Vb.m..........0...*.H.............P,........-f- 0480: .M8r...mn?..RP...z#....Y.HPG....RU...#...wi...$y..*..C..o...a. 04c0: B.3.|..../y...nC....z.n..B...Q](.{.|[email protected]<..FHx1.Q..W7. 0500: 44......j9.z.[.P:J.B..h.! ..;T.......k...........v.......UZ...
0540: 1E$9.u./.....H.(....?a.g.s...i.F...../..yY.(.X..c.B.....0...0..z
0580: .........AB...S.sj.....0....H........0?1$0"..U....Digital Signa
05c0: ture Trust Co.1.0...U....DST Root CA X30...160317164046Z..210317
0600: 164046Z0J1.0...U....US1.0...U....Let's Encrypt1#0!..U....Let's E
0640: ncrypt Authority X30.."0...
.H.............0............Z..G.r]7
0680: ..hc0..5&.%...5.p./..KA....5.X..*.h....u....bq.y........xgq.i.. 06c0: ......<H.
.Mw.$.G.Z....7....{....J..A.6....m<.h.#B...tg...Ra
0700: ..?e.......V.....?.......k...}.+.e...6u.k.J...Ix/..O
%)..t..1..
0740: 18....3.C.....y1.=-6....3j.91......d.3...).....}..........}0..
0780: y0...U.......0.......0...U.............+........s0q02..+.....0
07c0: ..&http://isrg.trustid.ocsp.identrust.com0;..+.....0../http://ap
0800: ps.identrust.com/roots/dstrootcax3.p7c0...U.#..0.......{,q...K.u
0840: ......0T..U. .M0K0...g.....0?..+..........000...+........"http: 0880: //cps.root-x1.letsencrypt.org0<..U...50301./.-.+http://crl.ident 08c0: rust.com/DSTROOTCAX3CRL.crl0...U.......Jjc.}....9..Ee.....0...*. 0900: H..............3...cX8.....U.vV.pH.iG'{.$...Z.J.)7$tQ.bh...pg... 0940: .N(Q.........Z......j.j.>W#....b.......?..H....eb..T..*. ....... 0980: ..2...w..ye.+.(.:[email protected]...\A.tl[]._3.M..8./{,b.... 09c0: o%./...F=.~..z...zm..%......./X../,h&.K.......CJ.DNosz(...n{L} 0a00: .....D....4[.B.. <= Recv SSL data, 5 bytes (0x5) 0000: ..... <= Recv SSL data, 1 bytes (0x1) 0000: . == Info: TLSv1.3 (IN), TLS handshake, CERT verify (15): <= Recv SSL data, 264 bytes (0x108) 0000: .........E1... u...h.u3i.2..n..[+../.../oWY..u....A!9.....g...." 0040: ..Z][email protected]......(........_..|P.jxDD..vv.."4...u..(.k[..w......Ni 0080: ..Bg....p.fR..4./....|.<....Y..9.....6...+...84[...zy.#.)o...... 00c0: ^..+.<n_..I.'....1..l........|. ../..........V.|2......-o).... 0100: ...E..y. <= Recv SSL data, 5 bytes (0x5) 0000: ....E <= Recv SSL data, 1 bytes (0x1) 0000: . == Info: TLSv1.3 (IN), TLS handshake, Finished (20): <= Recv SSL data, 52 bytes (0x34) 0000: [email protected]..|c..y.V..............n.....?.......#.j. => Send SSL data, 5 bytes (0x5) 0000: ..... == Info: TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1): => Send SSL data, 1 bytes (0x1) 0000: . => Send SSL data, 5 bytes (0x5) 0000: ....E => Send SSL data, 1 bytes (0x1) 0000: . == Info: TLSv1.3 (OUT), TLS handshake, Finished (20): => Send SSL data, 52 bytes (0x34) 0000: ...0...H[.6..X.q.>....z.Q.#0...5.z..([email protected];m... == Info: SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 == Info: ALPN, server accepted to use h2 == Info: Server certificate: == Info: subject: CN=desec.io == Info: start date: Aug 7 15:13:40 2020 GMT == Info: expire date: Nov 5 15:13:40 2020 GMT == Info: subjectAltName: host "desec.io" matched cert's "desec.io" == Info: issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3 == Info: SSL certificate verify ok. == Info: Using HTTP2, server supports multi-use == Info: Connection state changed (HTTP/2 confirmed) == Info: Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0 => Send SSL data, 5 bytes (0x5) 0000: ....) => Send SSL data, 1 bytes (0x1) 0000: . => Send SSL data, 5 bytes (0x5) 0000: ...., => Send SSL data, 1 bytes (0x1) 0000: . => Send SSL data, 5 bytes (0x5) 0000: ..... => Send SSL data, 1 bytes (0x1) 0000: . == Info: Using Stream ID: 1 (easy handle 0x5564fc4ef2c0) => Send SSL data, 5 bytes (0x5) 0000: ..... => Send SSL data, 1 bytes (0x1) 0000: . => Send header, 229 bytes (0xe5) 0000: GET /api/v1/domains/ HTTP/2 001d: Host: desec.io 002d: user-agent: acme.sh/2.8.8 (https://github.com/acmesh-official/ac 006d: me.sh) 0075: authorization: Token mf9_pBSHJbKN9tOQ-MvAwn1AZWsh. 00a9: accept: application/json 00c3: content-type: application/json 00e3: <= Recv SSL data, 5 bytes (0x5) 0000: ..... <= Recv SSL data, 1 bytes (0x1) 0000: . == Info: TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): <= Recv SSL data, 265 bytes (0x109) 0000: .......,.c^..............x|*...,..........?d<9#ihi.,>..U.p..D..( 0040: }...sv.....w..v$U.....K..k.Y...)!.9|........).g.X.x@*......M.(.. 0080: .>}.NM..;......\.^....#..E.I....N+.T.s..he.H.^.K.\.%f......R.g@. 00c0: .,...?..|..$..."&3.......VY.{......;..b.H..$....1...C.g......^ 0100: ...b..... <= Recv SSL data, 5 bytes (0x5) 0000: ..... <= Recv SSL data, 1 bytes (0x1) 0000: . == Info: TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): <= Recv SSL data, 265 bytes (0x109) 0000: .......,.................x|*...,.........V..^.......s.....NE5... 0040: <.Z.NS....&....z.s.........C..e.2a..D>.@.!).F.b......o.....~_r.
0080: ,..m.........j..[.:...m..}6..v..4...........x..q...F(.x....4^.
00c0: .y..-p..@.(#...../3.2G...n.....f........9<.7|.....h...$~7,.1<..g
0100: #.....4..
== Info: old SSL session ID is stale, removing
<= Recv SSL data, 5 bytes (0x5)
0000: ....O
<= Recv SSL data, 1 bytes (0x1)
0000: .
== Info: Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
=> Send SSL data, 5 bytes (0x5)
0000: .....
=> Send SSL data, 1 bytes (0x1)
0000: .
== Info: HTTP/2 stream 0 was not closed cleanly: Unknown error code (err 1)
== Info: stopped the pause stream!
== Info: Connection #0 to host desec.io left intact
[Sun Sep 20 15:35:32 UTC 2020] ret='92'
[Sun Sep 20 15:35:32 UTC 2020] http response code
[Sun Sep 20 15:35:32 UTC 2020] response='[hidden](please add '--output-insecure' to see this value)'
[Sun Sep 20 15:35:32 UTC 2020] error https://desec.io/api/v1/domains/
[Sun Sep 20 15:35:32 UTC 2020] invalid domain
[Sun Sep 20 15:35:32 UTC 2020] Error add txt for domain:_acme-challenge.condolencesghana.com
[Sun Sep 20 15:35:32 UTC 2020] _on_issue_err
[Sun Sep 20 15:35:32 UTC 2020] Please add '--debug' or '--log' to check more details.
[Sun Sep 20 15:35:32 UTC 2020] See: https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh
[Sun Sep 20 15:35:32 UTC 2020] _chk_vlist='condolencesghana.com#ulMUeyWWYcX554vdKv3AurG394glb8ySYmCBX33ajr8.VjCOrm00vRpSekg28xdPnC2564HxSqqJbuaVYCW3eug#https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/115539037/N7HL-Q#dns-01#dns_desec,'

@devudopw
Copy link

I can't seem to get DNS API/DESEC working when running it from inside a docker container. This is the command I use
docker run --rm -it -v "$(pwd)/acme.sh":/acme.sh --net=host neilpang/acme.sh --issue --staging --dns dns_desec -d condolencesghana.com --debug 2

The debug info says there is a libcurl error with code 92 when it tries to connect to "https://desec.io/api/v1/domains". Upon looking at the dns_desec.sh file, I believe the error originates from line 159. Is it a question of outdated docker image?

have you try #2925 this, change TTL to 3600

@willbrowningme
Copy link
Contributor

I'm just looking at the instructions here - https://github.com/acmesh-official/acme.sh/wiki/dnsapi#71-use-desecio and wondering why export DEDYN_NAME=foobar.dedyn.io is needed.

I can see at the moment in dns_desec.sh it's used for making API requests. However, would it not make more sense to use the $_domain variable that is already retrieved from the _get_root function?

The reason I'm thinking this behaviour would be preferable is because once the DEDYN_NAME variable is saved in the account.conf file, it is limited to renewing just that domain since all API requests will be sent to that zone.

So if you have multiple different wildcard domains to renew you would need to export the correct DEDYN_NAME variable before each renewal is run otherwise the TXT records will be added to the incorrect zone.

@mocaadmin
Copy link

I was trying to use the script for updating several domains. I confirm the above finding. The DEDYN_NAME is needed for login, but the API-requests should look like this:
line 66: if _desec_rest PUT "$REST_API/**$_domain**/rrsets/" "$body"; then
This also applies to the API-request in the rm-function.
I have adjusted it for my script, any chance to have this in the official version?

@willbrowningme
Copy link
Contributor

@mocaadmin this has been merged and fixed in the latest release so you can simply run acme.sh --upgrade to update.

@JNail
Copy link

JNail commented Aug 4, 2024

I ran into a, what I believe, new issue with the DESEC API client script.

Context

I have the following setup: My domains are delegated to DESEC as DNS provider. I installed the latest version of Proxmox 8.2.2 this includes the libproxmox-acme-plugins 1.5.0. I am currently trying to retrieve SSL certificates via a DNS-01 challenge for my Proxmox Server which is not reachable from the internet via Let's Encrypt. Proxmox uses the acme.sh script to perform Let's Encrypt certificate retrieval.

Issue

If I try to retrieve a new certificate, the verification step of Let's Encrypt fails

Loading ACME account details
Placing ACME order
Order URL: https://acme-staging-v02.api.letsencrypt.org/acme/order/157902903/18156594743

Getting authorization details from 'https://acme-staging-v02.api.letsencrypt.org/acme/authz-v3/13408785633'
The validation for pve.n-netcom.de is pending!
[Fri Aug  2 00:28:02 CEST 2024] Using desec.io api
[Fri Aug  2 00:28:03 CEST 2024] Adding record
[Fri Aug  2 00:28:03 CEST 2024] Added, OK
Add TXT record: _acme-challenge.pve.n-netcom.de
Sleeping 30 seconds to wait for TXT record propagation
Triggering validation
Sleeping for 5 seconds
[Fri Aug  2 00:28:39 CEST 2024] Using desec.io api
[Fri Aug  2 00:28:40 CEST 2024] Deleting record
[Fri Aug  2 00:28:40 CEST 2024] Deleted, OK
Remove TXT record: _acme-challenge.pve.n-netcom.de
TASK ERROR: validating challenge 'https://acme-staging-v02.api.letsencrypt.org/acme/authz-v3/13408785633' failed - status: invalid

What I found out is

When registering the TXT record with DESEC API, the TXT record name is truncated. Only the host-part is used, not the FQDN. The resulting TXT record subname, in the example above, is set as _acme-challenge.pve the content is set correctly.

I tracked the issue down to the following point:
acme.sh hands over the challenge in a correct way. Also the _get_root() seems to work correctly. But in line 62, where the header is built, the subname and the _sub_domain are concatenated, resulting in the truncated TXT record name.

Possible fix

I changed line 62 from
body="[{\"subname\":\"$_sub_domain\", \"type\":\"TXT\", \"records\":[$txtvalues], \"ttl\":3600}]"
to
body="[{\"subname\":\"$_sub_domain\":\"$_domain\", \"type\":\"TXT\", \"records\":[$txtvalues], \"ttl\":3600}]"
to add the domain and top-level domain part to the name of the TXT record beeing uploaded.

This works very well in my case.

I will also register a pull-request. Would be awesome if you could test it and bring the change mainline. Thanks!

Update:
While registering a pull-request I setup the automated tests in Github actions. Here I discovered that in the original form, despite seeming unsuccessful, the logs seem to state success when a wildcard certificate is requested. The issue I am facing seems to be only an issue for single host certificates and my suggested modification seems to break the wildcard certs.

JNail added a commit to JNail/acme.sh that referenced this issue Aug 4, 2024
…ed for the DNS-01 challenge at DESEC.io. See acmesh-official#2180 (comment) for details.
@JNail
Copy link

JNail commented Aug 6, 2024

Okay, new findings. I tried to issue a certificate with the OPNsense integrated acme.sh. It fails exactly in the same way than the one which is integrated into Proxmox. Just one difference, the logs are a bit more verbose and the standard setup of OPNsense is to make acme.sh check for the propagation of the TXT record every 10 seconds. This leads in my case to the first read attempt failing, and the second one to be a success. So the dns_desec.sh thinks that the TXT record is setup correctly and then continues to let Let's encrypt try to validate the record. Let's encrypt then fails as the domain and top-level-domain parts are missing in the TXT record in the DNS server.

Anyone has seen the same behaviour?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3rd party api report bugs to dns api, deploy hooks and notification hooks
Projects
None yet
Development

No branches or pull requests

9 participants