@@ -906,7 +906,7 @@ mod tests {
906906 } ;
907907 use std:: collections:: HashMap ;
908908 use std:: fs;
909- use std:: path:: PathBuf ;
909+ use std:: path:: { Path , PathBuf } ;
910910
911911 fn cdi_devices_config ( device_ids : & [ & str ] ) -> prost_types:: Struct {
912912 prost_types:: Struct {
@@ -1554,6 +1554,157 @@ mod tests {
15541554 let _ = fs:: remove_file ( socket_path) ;
15551555 }
15561556
1557+ /// Write a valid `user:pass` credential to a unique path for proxy-auth
1558+ /// secret tests. Caller removes it.
1559+ fn write_proxy_auth_file ( test_name : & str ) -> PathBuf {
1560+ let path = crate :: test_utils:: unique_socket_path ( test_name) . with_extension ( "auth" ) ;
1561+ fs:: write ( & path, "user:pass\n " ) . expect ( "write proxy auth file" ) ;
1562+ path
1563+ }
1564+
1565+ fn proxy_auth_config ( socket_path : PathBuf , auth_file : & Path ) -> PodmanComputeConfig {
1566+ PodmanComputeConfig {
1567+ socket_path,
1568+ stop_timeout_secs : 10 ,
1569+ proxy_auth_file : Some ( auth_file. to_string_lossy ( ) . into_owned ( ) ) ,
1570+ proxy_auth_allow_insecure : Some ( true ) ,
1571+ ..PodmanComputeConfig :: default ( )
1572+ }
1573+ }
1574+
1575+ fn plain_sandbox ( id : & str , name : & str ) -> DriverSandbox {
1576+ DriverSandbox {
1577+ id : id. to_string ( ) ,
1578+ name : name. to_string ( ) ,
1579+ namespace : String :: new ( ) ,
1580+ spec : None ,
1581+ status : None ,
1582+ }
1583+ }
1584+
1585+ fn secret_delete_request ( sandbox_id : & str ) -> String {
1586+ format ! (
1587+ "DELETE {}" ,
1588+ api_path( & format!(
1589+ "/libpod/secrets/{}" ,
1590+ container:: proxy_auth_secret_name( sandbox_id)
1591+ ) )
1592+ )
1593+ }
1594+
1595+ #[ tokio:: test]
1596+ async fn create_sandbox_removes_proxy_auth_secret_on_container_create_failure ( ) {
1597+ // A credential secret is staged before the container is created, so a
1598+ // container-create failure must remove it — no credential residue.
1599+ let sandbox_id = "sandbox-cc" ;
1600+ let auth_file = write_proxy_auth_file ( "create-fail" ) ;
1601+ let ( socket_path, request_log, handle) = spawn_podman_stub (
1602+ "create-container-fail" ,
1603+ vec ! [
1604+ StubResponse :: new( StatusCode :: OK , "{}" ) , // pull supervisor image
1605+ StubResponse :: new( StatusCode :: OK , "{}" ) , // pull sandbox image
1606+ StubResponse :: new( StatusCode :: CREATED , "{}" ) , // create volume
1607+ StubResponse :: new( StatusCode :: CREATED , "{}" ) , // create proxy-auth secret
1608+ StubResponse :: new( StatusCode :: INTERNAL_SERVER_ERROR , r#"{"message":"boom"}"# ) , // create container
1609+ StubResponse :: new( StatusCode :: NO_CONTENT , "" ) , // cleanup: remove volume
1610+ StubResponse :: new( StatusCode :: NO_CONTENT , "" ) , // cleanup: remove proxy-auth secret
1611+ ] ,
1612+ ) ;
1613+ let driver = test_driver_with_config ( proxy_auth_config ( socket_path. clone ( ) , & auth_file) ) ;
1614+
1615+ driver
1616+ . create_sandbox ( & plain_sandbox ( sandbox_id, "demo" ) )
1617+ . await
1618+ . expect_err ( "container create should fail" ) ;
1619+
1620+ handle. await . expect ( "stub task should finish" ) ;
1621+ let requests = request_log
1622+ . lock ( )
1623+ . expect ( "request log lock should not be poisoned" )
1624+ . clone ( ) ;
1625+ assert ! (
1626+ requests. contains( & secret_delete_request( sandbox_id) ) ,
1627+ "proxy-auth secret must be removed on container-create failure: {requests:?}"
1628+ ) ;
1629+ let _ = fs:: remove_file ( & auth_file) ;
1630+ let _ = fs:: remove_file ( socket_path) ;
1631+ }
1632+
1633+ #[ tokio:: test]
1634+ async fn create_sandbox_removes_proxy_auth_secret_on_start_failure ( ) {
1635+ // The container is created but fails to start; the staged credential
1636+ // secret must still be removed.
1637+ let sandbox_id = "sandbox-sf" ;
1638+ let auth_file = write_proxy_auth_file ( "start-fail" ) ;
1639+ let ( socket_path, request_log, handle) = spawn_podman_stub (
1640+ "create-start-fail" ,
1641+ vec ! [
1642+ StubResponse :: new( StatusCode :: OK , "{}" ) , // pull supervisor image
1643+ StubResponse :: new( StatusCode :: OK , "{}" ) , // pull sandbox image
1644+ StubResponse :: new( StatusCode :: CREATED , "{}" ) , // create volume
1645+ StubResponse :: new( StatusCode :: CREATED , "{}" ) , // create proxy-auth secret
1646+ StubResponse :: new( StatusCode :: CREATED , "{}" ) , // create container
1647+ StubResponse :: new( StatusCode :: INTERNAL_SERVER_ERROR , r#"{"message":"boom"}"# ) , // start container
1648+ StubResponse :: new( StatusCode :: NO_CONTENT , "" ) , // cleanup: remove container
1649+ StubResponse :: new( StatusCode :: NO_CONTENT , "" ) , // cleanup: remove volume
1650+ StubResponse :: new( StatusCode :: NO_CONTENT , "" ) , // cleanup: remove proxy-auth secret
1651+ ] ,
1652+ ) ;
1653+ let driver = test_driver_with_config ( proxy_auth_config ( socket_path. clone ( ) , & auth_file) ) ;
1654+
1655+ driver
1656+ . create_sandbox ( & plain_sandbox ( sandbox_id, "demo" ) )
1657+ . await
1658+ . expect_err ( "container start should fail" ) ;
1659+
1660+ handle. await . expect ( "stub task should finish" ) ;
1661+ let requests = request_log
1662+ . lock ( )
1663+ . expect ( "request log lock should not be poisoned" )
1664+ . clone ( ) ;
1665+ assert ! (
1666+ requests. contains( & secret_delete_request( sandbox_id) ) ,
1667+ "proxy-auth secret must be removed on start failure: {requests:?}"
1668+ ) ;
1669+ let _ = fs:: remove_file ( & auth_file) ;
1670+ let _ = fs:: remove_file ( socket_path) ;
1671+ }
1672+
1673+ #[ tokio:: test]
1674+ async fn delete_sandbox_removes_proxy_auth_secret ( ) {
1675+ // Deleting a sandbox (here already gone out of band) must remove the
1676+ // per-sandbox proxy-auth secret so credentials never outlive it.
1677+ let sandbox_id = "sandbox-del" ;
1678+ let ( socket_path, request_log, handle) = spawn_podman_stub (
1679+ "delete-proxy-auth" ,
1680+ vec ! [
1681+ StubResponse :: new( StatusCode :: NOT_FOUND , r#"{"message":"gone"}"# ) , // inspect
1682+ StubResponse :: new( StatusCode :: NOT_FOUND , r#"{"message":"gone"}"# ) , // stop
1683+ StubResponse :: new( StatusCode :: NOT_FOUND , r#"{"message":"gone"}"# ) , // remove container
1684+ StubResponse :: new( StatusCode :: NO_CONTENT , "" ) , // remove volume
1685+ StubResponse :: new( StatusCode :: NO_CONTENT , "" ) , // remove token secret
1686+ StubResponse :: new( StatusCode :: NO_CONTENT , "" ) , // remove proxy-auth secret
1687+ ] ,
1688+ ) ;
1689+ let driver = test_driver ( socket_path. clone ( ) ) ;
1690+
1691+ driver
1692+ . delete_sandbox ( sandbox_id, "demo" )
1693+ . await
1694+ . expect ( "delete should succeed" ) ;
1695+
1696+ handle. await . expect ( "stub task should finish" ) ;
1697+ let requests = request_log
1698+ . lock ( )
1699+ . expect ( "request log lock should not be poisoned" )
1700+ . clone ( ) ;
1701+ assert ! (
1702+ requests. contains( & secret_delete_request( sandbox_id) ) ,
1703+ "proxy-auth secret must be removed on delete: {requests:?}"
1704+ ) ;
1705+ let _ = fs:: remove_file ( socket_path) ;
1706+ }
1707+
15571708 #[ tokio:: test]
15581709 async fn delete_sandbox_uses_request_id_when_container_label_disagrees ( ) {
15591710 let sandbox_id = "sandbox-request-id" ;
0 commit comments