From nobody Sun Apr 03 19:48:42 2022 X-Original-To: jail@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 1004B1A5101C for ; Sun, 3 Apr 2022 19:48:59 +0000 (UTC) (envelope-from Alexander@leidinger.net) Received: from mailgate.Leidinger.net (mailgate.leidinger.net [IPv6:2a00:1828:2000:313::1:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature ECDSA (P-256) client-digest SHA256) (Client CN "mailgate.leidinger.net", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4KWkxQ1N04z3JPt; Sun, 3 Apr 2022 19:48:58 +0000 (UTC) (envelope-from Alexander@leidinger.net) Received: from outgoing.leidinger.net (p508d4362.dip0.t-ipconnect.de [80.141.67.98]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-256) server-digest SHA256 client-signature ECDSA (P-256) client-digest SHA256) (Client CN "outgoing.leidinger.net", Issuer "R3" (verified OK)) by mailgate.Leidinger.net (Postfix) with ESMTPSA id 7F01A264C1; Sun, 3 Apr 2022 21:48:47 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=leidinger.net; s=outgoing-alex; t=1649015327; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type; bh=iV32RqFPNg5oh/9x17EPnh1lAglxLljd11X9QyvoEgM=; b=X605mG0pJa2fzZt8gFluqF6Fj3nhOP+bW0AY7xIRCSzyiGAOFPoAmdBP1s0MWs4fPfzGHt qRBq4zkixH1N+346mZn7RqHwyJJIW3LETJolRh5rMliVhNnS9nfKW8vztvPo3Q7qPxNeV6 TbI8+t6GCtb5VOr0j5xvfBEHbRmCPBcKg1ER0BEFhpmTawDPRUY71LXPSDDdR8wrPYTGMJ 23DyVbt6BEWpIMhcYGaJPveLmz6hxn26SXzZ79QEYKa/gFvYTlKi/Ff0zSQQAFFAHnVJqy MHbwd7RTCqgUoezuzvwB4kZAN1MI2fA+AQeWknMFncN9t47e41/2iUG6M82ZDA== Received: from webmail.leidinger.net (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-256) server-digest SHA256) (Client did not present a certificate) by outgoing.leidinger.net (Postfix) with ESMTPS id EBAC0C380; Sun, 3 Apr 2022 21:48:42 +0200 (CEST) Date: Sun, 03 Apr 2022 21:48:42 +0200 Message-ID: <20220403214842.Horde.vlwSVh0KOZ6sL7aDfgA9KKL@webmail.leidinger.net> From: Alexander Leidinger To: security@freebsd.org, jail@freebsd.org Subject: Auto-jailing of services - 2nd implementation Accept-Language: de,en Content-Type: multipart/signed; boundary="=_YVfRcdE_UVp6rdkJjuoW1Qz"; protocol="application/pgp-signature"; micalg=pgp-sha256 List-Id: Discussion about FreeBSD jail(8) List-Archive: https://lists.freebsd.org/archives/freebsd-jail List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-freebsd-jail@freebsd.org MIME-Version: 1.0 X-Rspamd-Queue-Id: 4KWkxQ1N04z3JPt X-Spamd-Bar: ------ Authentication-Results: mx1.freebsd.org; dkim=pass header.d=leidinger.net header.s=outgoing-alex header.b=X605mG0p; dmarc=pass (policy=quarantine) header.from=leidinger.net; spf=pass (mx1.freebsd.org: domain of Alexander@leidinger.net designates 2a00:1828:2000:313::1:5 as permitted sender) smtp.mailfrom=Alexander@leidinger.net X-Spamd-Result: default: False [-6.10 / 15.00]; RCVD_VIA_SMTP_AUTH(0.00)[]; R_SPF_ALLOW(-0.20)[+mx]; HAS_ATTACHMENT(0.00)[]; TO_DN_NONE(0.00)[]; RCVD_COUNT_THREE(0.00)[3]; MID_RHS_MATCH_FROMTLD(0.00)[]; DKIM_TRACE(0.00)[leidinger.net:+]; RCPT_COUNT_TWO(0.00)[2]; DMARC_POLICY_ALLOW(-0.50)[leidinger.net,quarantine]; NEURAL_HAM_SHORT(-1.00)[-1.000]; SIGNED_PGP(-2.00)[]; FROM_EQ_ENVFROM(0.00)[]; MIME_TRACE(0.00)[0:+,1:+,2:+,3:+,4:~]; ASN(0.00)[asn:34240, ipnet:2a00:1828::/32, country:DE]; RECEIVED_SPAMHAUS_PBL(0.00)[80.141.67.98:received]; ARC_NA(0.00)[]; NEURAL_HAM_MEDIUM(-1.00)[-1.000]; R_DKIM_ALLOW(-0.20)[leidinger.net:s=outgoing-alex]; FROM_HAS_DN(0.00)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; MIME_GOOD(-0.20)[multipart/signed,multipart/mixed,text/plain,text/diff]; MLMMJ_DEST(0.00)[jail]; RCVD_TLS_ALL(0.00)[] X-ThisMailContainsUnwantedMimeParts: N This message is in MIME format and has been PGP signed. --=_YVfRcdE_UVp6rdkJjuoW1Qz Content-Type: multipart/mixed; boundary="=_w3-e5ke9RnnaZj37w0YmaYo" This message is in MIME format. --=_w3-e5ke9RnnaZj37w0YmaYo Content-Type: text/plain; charset=utf-8; format=flowed; DelSp=Yes Content-Disposition: inline Content-Transfer-Encoding: quoted-printable Hi, attached is a new implementation of service jails (auto-jailing of=20=20 services).=20This one now supports rc command prefixes (e.g. onestart)=20= =20 and=20I tested it in nested jails. The benefit of auto-jailing services=20= =20 is,=20that you can apply some restrictions to services (and what other=20= =20 processes=20it may see). If your service requires access to network but=20= =20 not=20sysvipc, and it doesn't run as root, it can be limited to network=20= =20 access=20with or without raw sockets, filesystem-permitted files, and=20=20 doesn't=20see other processes on the system. For a few services I have added the required "svcj-config" in the=20=20 start=20scripts (e.g. network access for syslog by setting=20=20 syslogd_svj_options=3Dnet_basic). Possible svcj config options for service jails: + netv4) + _svcj_cmd_options=3D"ip4=3Dinherit allow.reserved_ports=20=20 ${_svcj_cmd_options}" +=09 ;; + netv6) + _svcj_cmd_options=3D"ip6=3Dinherit allow.reserved_ports=20=20 ${_svcj_cmd_options}" +=09 ;; + net_basic) + _svcj_cmd_options=3D"ip4=3Dinherit ip6=3Dinherit allow.reserved_ports= =20=20 ${_svcj_cmd_options}" +=09 ;; + net_raw) + _svcj_cmd_options=3D"allow.raw_sockets ${_svcj_cmd_options}" + ;; + net_all) + _svcj_cmd_options=3D"allow.socket_af allow.raw_sockets=20=20 allow.reserved_ports=20ip4=3Dinherit ip6=3Dinherit ${_svcj_cmd_options}" + ;; + sysvipc) + _svcj_cmd_options=3D"sysvmsg=3Dinherit sysvsem=3Dinherit=20=20 sysvshm=3Dinherit ${_svcj_cmd_options}" + ;; + mlock) + _svcj_cmd_options=3D"allow.mlock ${_svcj_cmd_options}" + ;; + vmm) + _svcj_cmd_options=3D"allow.vmm ${_svcj_cmd_options}" By setting syslogd_svcj=3D"YES" in rc.conf your syslogd will be started=20= =20 in=20a jail which inherits the full filesystem and the ipv4 and ipv6=20=20 addresses=20of the parent. It would be nice if interested people could experiment a little bit=20=20 with=20this, e.g. adding name_svcj_options=3D"X Y" from above and=20=20 name_svcj=3D"YES" into rc.conf and see if it works. Note, doing that for=20= =20 sshd=20doesn't make sense in the generic case, it wouldn't see your=20=20 jails.=20It may make sense for services. Any kind of feedback and tested name_svcj_options submissions welcome... Bye, Alexander. --=20 http://www.Leidinger.net=20Alexander@Leidinger.net: PGP 0x8F31830F9F2772BF http://www.FreeBSD.org netchild@FreeBSD.org : PGP 0x8F31830F9F2772BF --=_w3-e5ke9RnnaZj37w0YmaYo Content-Type: text/diff; charset=utf-8; name=svcj.diff Content-Disposition: attachment; size=9845; filename=svcj.diff Content-Transfer-Encoding: quoted-printable diff --git a/libexec/rc/rc.d/auditdistd b/libexec/rc/rc.d/auditdistd index 13cb5d5b69d..3218bd35755 100755 --- a/libexec/rc/rc.d/auditdistd +++ b/libexec/rc/rc.d/auditdistd @@ -19,4 +19,7 @@ required_files=3D"/etc/security/${name}.conf" extra_commands=3D"reload" =20 =20load_rc_config $name + +: ${auditdistd_svcj_options:=3D"net_basic"} + run_rc_command "$1" diff --git a/libexec/rc/rc.d/ftpd b/libexec/rc/rc.d/ftpd index dc623ea5943..a04c7ce5ee2 100755 --- a/libexec/rc/rc.d/ftpd +++ b/libexec/rc/rc.d/ftpd @@ -23,4 +23,7 @@ ftpd_prestart() } =20 =20load_rc_config $name + +: ${ftpd_svcj_options:=3D"net_all"} + run_rc_command "$1" diff --git a/libexec/rc/rc.d/inetd b/libexec/rc/rc.d/inetd index aa8ac20aeae..8cf7be5d91e 100755 --- a/libexec/rc/rc.d/inetd +++ b/libexec/rc/rc.d/inetd @@ -18,4 +18,7 @@ required_files=3D"/etc/${name}.conf" extra_commands=3D"reload" =20 =20load_rc_config $name + +: ${inetd_svcj_options:=3D"net_basic"} + run_rc_command "$1" diff --git a/libexec/rc/rc.d/kadmind b/libexec/rc/rc.d/kadmind index 773b2d0e499..1bdd420e415 100755 --- a/libexec/rc/rc.d/kadmind +++ b/libexec/rc/rc.d/kadmind @@ -26,4 +26,7 @@ kadmind_start_precmd() } =20 =20load_rc_config $name + +: ${kadmind_svcj_options:=3D"net_basic"} + run_rc_command "$1" diff --git a/libexec/rc/rc.d/kdc b/libexec/rc/rc.d/kdc index c2747ae08ca..11205d6e092 100755 --- a/libexec/rc/rc.d/kdc +++ b/libexec/rc/rc.d/kdc @@ -26,4 +26,7 @@ kdc_start_precmd() } =20 =20load_rc_config $name + +: ${kdc_svcj_options:=3D"net_basic"} + run_rc_command "$1" diff --git a/libexec/rc/rc.d/kpasswdd b/libexec/rc/rc.d/kpasswdd index a2875bf1515..af7b7a6b9aa 100755 --- a/libexec/rc/rc.d/kpasswdd +++ b/libexec/rc/rc.d/kpasswdd @@ -26,4 +26,7 @@ kpasswdd_start_precmd() } =20 =20load_rc_config $name + +: ${kapsswd_svcj_options:=3D"net_basic"} + run_rc_command "$1" diff --git a/libexec/rc/rc.d/local_unbound b/libexec/rc/rc.d/local_unbound index 19cb9a6c5c0..7436034495f 100755 --- a/libexec/rc/rc.d/local_unbound +++ b/libexec/rc/rc.d/local_unbound @@ -34,6 +34,7 @@ load_rc_config $name : ${local_unbound_anchor:=3D${local_unbound_workdir}/root.key} : ${local_unbound_forwarders:=3D} : ${local_unbound_tls:=3D} +: ${local_unbound_svcj_options:=3D"net_basic"} =20 =20do_as_unbound() { diff --git a/libexec/rc/rc.d/lpd b/libexec/rc/rc.d/lpd index fc8180cb221..725adda9072 100755 --- a/libexec/rc/rc.d/lpd +++ b/libexec/rc/rc.d/lpd @@ -25,4 +25,7 @@ chkprintcap() } =20 =20load_rc_config $name + +: ${lpd_svcj_options:=3D"net_basic"} + run_rc_command "$1" diff --git a/libexec/rc/rc.d/syslogd b/libexec/rc/rc.d/syslogd index 2351c086212..95d2b156b88 100755 --- a/libexec/rc/rc.d/syslogd +++ b/libexec/rc/rc.d/syslogd @@ -71,4 +71,7 @@ set_socketlist() echo $_socketargs } load_rc_config $name + +: ${syslogd_svcj_options:=3D"net_basic"} + run_rc_command "$1" diff --git a/libexec/rc/rc.subr b/libexec/rc/rc.subr index dc4f49612c2..f339738c0a3 100644 --- a/libexec/rc/rc.subr +++ b/libexec/rc/rc.subr @@ -51,6 +51,9 @@ PROTECT=3D"/usr/bin/protect" ID=3D"/usr/bin/id" IDCMD=3D"if [ -x $ID ]; then $ID -un; fi" PS=3D"/bin/ps -ww" +SERVICE=3D/usr/sbin/service +JAIL_CMD=3D/usr/sbin/jail +_svcj_generic_params=3D"path=3D/ mount.nodevfs host=3Dinherit" JID=3D0 # rc_service provides the path to the service script that we are executing= . # This is not being set here in an execution context, necessarily, so it's @@ -368,6 +371,16 @@ _find_processes() $_procname|$_procnamebn|${_procnamebn}:|"(${_procnamebn})"|"[${_proc= namebn}]")' fi =20 +=09if checkyesno ${name}_svcj; then + JID=3D$(/usr/sbin/jls -j svcj-${name} jid) + + case ${JID} in + ''|*[!0-9]*) + # svj-jail doesn't exist, fallback to host-check + JID=3D0 + ;; + esac + fi _proccheck=3D"\ $PS 2>/dev/null -o pid=3D -o jid=3D -o command=3D $_psargs"' | while read _npid _jid '"$_fp_args"'; do @@ -959,6 +972,18 @@ run_rc_command() _pidcmd=3D _procname=3D${procname:-${command}} =20 +=09# If a specifc jail has a specific svcj request, honor it (YES/NO). + # If not (variable empty), evaluate the global svcj catch-call. + # A global YES can be overriden by a specific NO, and a global NO is over= riden + # by a specific YES. + eval _svcj=3D\$${name}_svcj + if [ -z "$_svcj" ]; then + _svcj=3D${svcj_all_enable} + if [ -z "$_svcj" ]; then + eval ${name}_svcj=3DNO + fi + fi + # setup pid check command if [ -n "$_procname" ]; then if [ -n "$pidfile" ]; then @@ -994,7 +1019,7 @@ run_rc_command() _fib=3D\$${name}_fib _env=3D\$${name}_env \ _prepend=3D\$${name}_prepend _login_class=3D\${${name}_login_class:-d= aemon} \ _limits=3D\$${name}_limits _oomprotect=3D\$${name}_oomprotect \ - _env_file=3D\$${name}_env_file + _env_file=3D\$${name}_env_file _svcj_options=3D\$${name}_svcj_options =20 =20 if [ -n "$_env_file" ] && [ -r "${_env_file}" ]; then # load env from f= ile set -a @@ -1008,6 +1033,42 @@ run_rc_command() fi fi =20 +=09if [ -n "$_svcj_options" ]; then # translate service jail options + _svcj_cmd_options=3D"" + + for _svcj_option in $_svcj_options; do + case "$_svcj_option" in + netv4) + _svcj_cmd_options=3D"ip4=3Dinherit allow.reserved_ports ${_svcj_cmd_o= ptions}" + ;; + netv6) + _svcj_cmd_options=3D"ip6=3Dinherit allow.reserved_ports ${_svcj_cmd_o= ptions}" + ;; + net_basic) + _svcj_cmd_options=3D"ip4=3Dinherit ip6=3Dinherit allow.reserved_ports= ${_svcj_cmd_options}" + ;; + net_raw) + _svcj_cmd_options=3D"allow.raw_sockets ${_svcj_cmd_options}" + ;; + net_all) + _svcj_cmd_options=3D"allow.socket_af allow.raw_sockets allow.reserved= _ports ip4=3Dinherit ip6=3Dinherit ${_svcj_cmd_options}" + ;; + sysvipc) + _svcj_cmd_options=3D"sysvmsg=3Dinherit sysvsem=3Dinherit sysvshm=3Din= herit ${_svcj_cmd_options}" + ;; + mlock) + _svcj_cmd_options=3D"allow.mlock ${_svcj_cmd_options}" + ;; + vmm) + _svcj_cmd_options=3D"allow.vmm ${_svcj_cmd_options}" + ;; + *) + echo ${name}: unknown service jail option: $_svcj_option + ;; + esac + done + fi + [ -z "$autoboot" ] && eval $_pidcmd # determine the pid if necessary =20 =20 for _elem in $_keywords; do @@ -1053,9 +1114,50 @@ run_rc_command() if [ -n "$_env" ]; then eval "export -- $_env" fi - _run_rc_precmd || return 1 - _run_rc_doit "$_cmd $rc_extra_args" || return 1 - _run_rc_postcmd + + if [ "${_rc_svcj}" !=3D jailing ]; then + _run_rc_precmd || return 1 + fi + if ! checkyesno ${name}_svcj; then + _run_rc_doit "$_cmd $rc_extra_args" || return 1 + else + case "$rc_arg" in + start) + if [ "${_rc_svcj}" !=3D jailing ]; then + _return=3D1 + $JAIL_CMD -c $_svcj_generic_params $_svcj_cmd_options \ + exec.start=3D"export _rc_svcj=3Djailing; for d in /etc/rc.d $loc= al_startup; do [ -x \$d/${name} ] && \$d/${name} ${_rc_prefix}start $rc_ext= ra_args && break; done" \ + exec.stop=3D"export _rc_svcj=3Djailing; for d in /etc/rc.d $loca= l_startup; do [ -x \$d/${name} ] && \$d/${name} ${_rc_prefix}stop $rc_extra= _args && break; done" \ + exec.consolelog=3D"/var/log/svcj_${name}_console.log" \ + name=3Dsvcj-${name} && _return=3D0 + else + _run_rc_doit "$_cmd $rc_extra_args" || _return=3D1 + fi + ;; + stop) + if [ "${_rc_svcj}" !=3D jailing ]; then + $SERVICE -j svcj-${name} ${name} ${_rc_prefix}stop $rc_extra_args ||= _return=3D1 + $JAIL_CMD -r svcj-${name} 2>/dev/null + else + _run_rc_doit "$_cmd $rc_extra_args" || _return=3D1 + fi + ;; + restart|status) ;; # no special case needed for svcj or handled somewh= ere else + *) +if checkyesno ${name}_svcj; then +echo XXX: check if \"$rc_arg\" needs to be executed in the jail or outside +fi +# if [ "${_rc_svcj}" !=3D jailing ]; then +# $SERVICE -j svcj-${name} ${name} ${_rc_prefix}${rc_arg} $rc_extra_a= rgs || _return=3D1 +# else + _run_rc_doit "$_cmd $rc_extra_args" || _return=3D1 +# fi + ;; + esac + fi + if [ "${_rc_svcj}" !=3D jailing ]; then + _run_rc_postcmd + fi return $_return fi =20 @@=20-1113,9 +1215,21 @@ run_rc_command() return 1 fi =20 -=09 if ! _run_rc_precmd; then - warn "failed precmd routine for ${name}" - return 1 + if [ "${_rc_svcj}" !=3D jailing ]; then + if ! _run_rc_precmd; then + warn "failed precmd routine for ${name}" + return 1 + fi + fi + + if checkyesno ${name}_svcj; then + if [ "${_rc_svcj}" !=3D jailing ]; then + $JAIL_CMD -c $_svcj_generic_params $_svcj_cmd_options\ + exec.start=3D"export _rc_svcj=3Djailing; for d in /etc/rc.d $loca= l_startup; do [ -x \$d/${name} ] && \$d/${name} ${_rc_prefix}start $rc_extr= a_args && break; done" \ + exec.stop=3D"export _rc_svcj=3Djailing; for d in /etc/rc.d $local= _startup; do [ -x \$d/${name} ] && \$d/${name} ${_rc_prefix}stop $rc_extra_= args && break; done" \ + exec.consolelog=3D"/var/log/svcj_${name}_console.log" \ + name=3Dsvcj-${name} || return 1 + fi fi =20 =20 # setup the full command to run @@ -1152,16 +1266,28 @@ $command $rc_flags $command_args" # Prepend default limits _doit=3D"$_cd limits -C $_login_class $_limits $_doit" =20 + +=09 local _really_run_it=3Dtrue + if checkyesno ${name}_svcj; then + if [ "${_rc_svcj}" !=3D jailing ]; then + _really_run_it=3Dfalse + fi + fi + + if [ "$_really_run_it" =3D true ]; then # run the full command # - if ! _run_rc_doit "$_doit"; then - warn "failed to start ${name}" - return 1 + if ! _run_rc_doit "$_doit"; then + warn "failed to start ${name}" + return 1 + fi fi =20 +=09 if [ "${_rc_svcj}" !=3D jailing ]; then # finally, run postcmd # - _run_rc_postcmd + _run_rc_postcmd + fi ;; =20 =20 stop) @@ -1183,6 +1309,11 @@ $command $rc_flags $command_args" # and run postcmd. wait_for_pids $rc_pid =20 +=09 if checkyesno ${name}_svcj; then + # remove service jail + $JAIL_CMD -r svcj-${name} 2>/dev/null + fi + _run_rc_postcmd ;; =20 --=_w3-e5ke9RnnaZj37w0YmaYo-- --=_YVfRcdE_UVp6rdkJjuoW1Qz Content-Type: application/pgp-signature Content-Description: Digitale PGP-Signatur Content-Disposition: inline -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIzBAABCAAdFiEER9UlYXp1PSd08nWXEg2wmwP42IYFAmJJ+hkACgkQEg2wmwP4 2IYpnA//WhZ/G/sLzATWcHOtHntm3S1hVQNJCUy9dHKbtkpyXSWHkfenSTDlLqeb 2k3VlnH5qmHvcg0bT67Za53o9uqm3TwdX4/L7SOAwL4EgsgzHMBIuuG+A8UIkUiP KOk0XFTFKdkRe1VSH09Xf8ol2EeLZ/fpIsKGTLq0L32CUhbcl+P616RVrxrz6GJW Pze2YlTK885T0jWGqCFkJ3MtycOiId9HJP1K+4eCuQoWjfvzXQVpnueKt+jEQJEM xzcp9VMlOOrLxEdpjyMxCoyfu4czQG9aeEznoAww6npSUmjcQanXnVi/fkOjeXbM /Y4tnSK0mEHa4w1n/YIdcUTElbzUOF2qy4JdTfaRrgdumWF/2wV+GQ9YFEmR6MSZ I0ORHT/r684dAxbRLzk2g4yL9wrvRPUSrYru4n+bxaqDH79sCodzpjOM3wgN2fGy o7yRNuAexv7X+ROcDan/hbWs2YdXBz1qJ/ofepf4K/HxUtElsruL8tBATZDzXg0y Prk1TSUSIQXEauSG/FoQxMnXidVFCvIbUHxZs2UzH8yBVKPk1zsWmWZwOYzsnU5R uqyOZfxhzvgZO3g0dQ4uf7aBGvS+riTUFiBAVe4yt21AP6WvY3NFTLB4fH7C11z5 wYz0BAWpAHY8BNG+gPqK1631yrelC070bPFw9S3LoPwhUbzke44= =2g/S -----END PGP SIGNATURE----- --=_YVfRcdE_UVp6rdkJjuoW1Qz--